https://bugs.gentoo.org/901035 https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3322 https://gitlab.gnome.org/GNOME/glib/-/commit/cc7f2f81cc59751fcc689731dcd60af5da5723ba From cc7f2f81cc59751fcc689731dcd60af5da5723ba Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Mon, 13 Mar 2023 16:23:37 +0800 Subject: [PATCH] gstrfuncs: Improve inline version of g_strdup() to avoid breaking C++ code Wrap the logic into a G_ALWAYS_INLINE function, instead of using a complex statement-expression which is not allowed in braced initializer lists and expanded into some bad thing when it's used as `::g_strdup(...)`. We cannot use `__builtin_constant_p (str)` because GCC documentation clearly states that it always produces 0 when str is a const char * argument of an inline function. But `__builtin_constant_p (!str)`, `__builtin_constant_p (!!str)`, and `__builtin_constant_p (strlen (str))` functions properly with `-O1` or above enabled. Fixes #2936. --- a/glib/gstrfuncs.h +++ b/glib/gstrfuncs.h @@ -204,23 +204,6 @@ gboolean (g_str_has_prefix) (const gchar *str, (g_str_has_suffix) (STR, SUFFIX) \ ) -#define g_strdup(STR) \ - (__builtin_constant_p ((STR)) ? \ - (G_LIKELY ((STR) != NULL) ? \ - G_GNUC_EXTENSION ({ \ - const char *const ___str = ((STR)); \ - const char *const __str = _G_STR_NONNULL (___str); \ - const size_t __str_len = strlen (__str) + 1; \ - char *__dup_str = (char *) g_malloc (__str_len); \ - (char *) memcpy (__dup_str, __str, __str_len); \ - }) \ - : \ - (char *) (NULL) \ - ) \ - : \ - (g_strdup) ((STR)) \ - ) - #endif /* !defined (__GI_SCANNER__) */ #endif /* !defined (__GTK_DOC_IGNORE__) */ #endif /* G_GNUC_CHECK_VERSION (2, 0) */ @@ -318,6 +301,32 @@ GLIB_AVAILABLE_IN_ALL gchar* g_strjoin (const gchar *separator, ...) G_GNUC_MALLOC G_GNUC_NULL_TERMINATED; +#if G_GNUC_CHECK_VERSION(2, 0) +#ifndef __GTK_DOC_IGNORE__ +#ifndef __GI_SCANNER__ + +G_ALWAYS_INLINE static inline char * +g_strdup_inline (const char *str) +{ + if (__builtin_constant_p (!str) && !str) + return NULL; + + if (__builtin_constant_p (!!str) && !!str && __builtin_constant_p (strlen (str))) + { + const size_t len = strlen (str) + 1; + char *dup_str = (char *) g_malloc (len); + return (char *) memcpy (dup_str, str, len); + } + + return g_strdup (str); +} + +#define g_strdup(x) g_strdup_inline (x) + +#endif /* !defined (__GI_SCANNER__) */ +#endif /* !defined (__GTK_DOC_IGNORE__) */ +#endif /* G_GNUC_CHECK_VERSION (2, 0) */ + /* Make a copy of a string interpreting C string -style escape * sequences. Inverse of g_strescape. The recognized sequences are \b * \f \n \r \t \\ \" and the octal format. --- a/glib/tests/cxx.cpp +++ b/glib/tests/cxx.cpp @@ -349,6 +349,36 @@ test_strdup_macro (void) g_free (str); } +static void +test_strdup_macro_qualified (void) +{ + gchar *str; + + g_assert_null (::g_strdup (NULL)); + + str = ::g_strdup ("C++ is cool too!"); + g_assert_nonnull (str); + g_assert_cmpstr (str, ==, "C++ is cool too!"); + g_free (str); +} + +static void +test_strdup_macro_nested_initializer (void) +{ + struct + { + char *p, *q; + } strings = { + g_strdup (NULL), + g_strdup ("C++ is cool too!"), + }; + + g_assert_null (strings.p); + g_assert_nonnull (strings.q); + g_assert_cmpstr (strings.q, ==, "C++ is cool too!"); + g_free (strings.q); +} + static void test_str_has_prefix (void) { @@ -527,6 +557,8 @@ main (int argc, char *argv[]) g_test_add_func ("/C++/str-equal", test_str_equal); g_test_add_func ("/C++/strdup", test_strdup); g_test_add_func ("/C++/strdup/macro", test_strdup_macro); + g_test_add_func ("/C++/strdup/macro/qualified", test_strdup_macro_qualified); + g_test_add_func ("/C++/strdup/macro/nested-initializer", test_strdup_macro_nested_initializer); g_test_add_func ("/C++/str-has-prefix", test_str_has_prefix); g_test_add_func ("/C++/str-has-prefix/macro", test_str_has_prefix_macro); g_test_add_func ("/C++/str-has-suffix", test_str_has_suffix); -- GitLab