[retro-gtk] Add and use retro_try()



commit 30977ebe548784e7b7d0ac6e57923a651e38979d
Author: Adrien Plazas <kekun plazas laposte net>
Date:   Tue Dec 22 10:20:18 2020 +0100

    Add and use retro_try()
    
    This simplifies error handling and hardens it by simulating a try/catch
    block pair. This also automatically fixes some error leaks.
    
    This also adds retro_try_propagate() and retro_try_propagate_val() to
    simplify the most common use-case of retro_try().

 retro-gtk/retro-core-descriptor.c | 365 +++++++++++++++-----------------------
 retro-gtk/retro-core.c            |  41 ++---
 retro-gtk/retro-gl-display.c      |  12 +-
 retro-gtk/retro-glsl-filter.c     |  81 ++++-----
 retro-gtk/retro-glsl-shader.c     |  20 +--
 retro-gtk/retro-module-iterator.c |  68 ++++---
 retro-gtk/retro-runner-process.c  |  37 ++--
 retro-runner/retro-core.c         | 164 +++++++----------
 retro-runner/retro-game-info.c    |  15 +-
 shared/retro-error-private.h      | 256 ++++++++++++++++++++++++++
 tests/retro-reftest-file.c        | 142 ++++++---------
 11 files changed, 648 insertions(+), 553 deletions(-)
---
diff --git a/retro-gtk/retro-core-descriptor.c b/retro-gtk/retro-core-descriptor.c
index 9081153..8d8923f 100644
--- a/retro-gtk/retro-core-descriptor.c
+++ b/retro-gtk/retro-core-descriptor.c
@@ -9,6 +9,8 @@
 
 #include "retro-core-descriptor.h"
 
+#include "retro-error-private.h"
+
 struct _RetroCoreDescriptor
 {
   GObject parent_instance;
@@ -101,22 +103,18 @@ has_key_prefixed (RetroCoreDescriptor  *self,
 {
   g_autofree gchar *group = NULL;
   gboolean has_key;
-  GError *tmp_error = NULL;
 
   g_assert (group_prefix != NULL);
   g_assert (group_suffix != NULL);
   g_assert (key != NULL);
 
   group = g_strconcat (group_prefix, group_suffix, NULL);
-  has_key = g_key_file_has_key (self->key_file,
-                                group,
-                                key,
-                                &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return FALSE;
-  }
+  retro_try_propagate_val ({
+    has_key = g_key_file_has_key (self->key_file,
+                                  group,
+                                  key,
+                                  &catch);
+  }, catch, error, FALSE);
 
   return has_key;
 }
@@ -130,7 +128,6 @@ get_string_prefixed (RetroCoreDescriptor  *self,
 {
   g_autofree gchar *group = NULL;
   g_autofree gchar *string = NULL;
-  GError *tmp_error = NULL;
 
   g_assert (group_prefix != NULL);
   g_assert (group_suffix != NULL);
@@ -138,12 +135,14 @@ get_string_prefixed (RetroCoreDescriptor  *self,
 
   group = g_strconcat (group_prefix, group_suffix, NULL);
 
-  string = g_key_file_get_string (self->key_file,
-                                  group,
-                                  key,
-                                  &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL))
+  retro_try ({
+    string = g_key_file_get_string (self->key_file,
+                                    group,
+                                    key,
+                                    &catch);
+  }, catch, {
     return NULL;
+  });
 
   return g_steal_pointer (&string);
 }
@@ -158,7 +157,6 @@ get_string_list_prefixed (RetroCoreDescriptor  *self,
 {
   g_autofree gchar *group = NULL;
   g_auto (GStrv) list = NULL;
-  GError *tmp_error = NULL;
 
   g_assert (group_prefix != NULL);
   g_assert (group_suffix != NULL);
@@ -166,16 +164,13 @@ get_string_list_prefixed (RetroCoreDescriptor  *self,
   g_assert (length != NULL);
 
   group = g_strconcat (group_prefix, group_suffix, NULL);
-  list = g_key_file_get_string_list (self->key_file,
-                                     group,
-                                     key,
-                                     length,
-                                     &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return NULL;
-  }
+  retro_try_propagate_val ({
+    list = g_key_file_get_string_list (self->key_file,
+                                       group,
+                                       key,
+                                       length,
+                                       &catch);
+  }, catch, error, NULL);
 
   return g_steal_pointer (&list);
 }
@@ -187,20 +182,16 @@ check_has_required_key (RetroCoreDescriptor  *self,
                         GError              **error)
 {
   gboolean has_key;
-  GError *tmp_error = NULL;
 
   g_assert (group != NULL);
   g_assert (key != NULL);
 
-  has_key = g_key_file_has_key (self->key_file,
-                                LIBRETRO_GROUP,
-                                TYPE_KEY,
-                                &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return;
-  }
+  retro_try_propagate ({
+    has_key = g_key_file_has_key (self->key_file,
+                                  LIBRETRO_GROUP,
+                                  TYPE_KEY,
+                                  &catch);
+  }, catch, error);
 
   if (!has_key)
     g_set_error (error,
@@ -217,47 +208,33 @@ static void
 check_libretro_group (RetroCoreDescriptor  *self,
                       GError              **error)
 {
-  GError *tmp_error = NULL;
-
-  check_has_required_key (self,
-                          LIBRETRO_GROUP,
-                          TYPE_KEY,
-                          &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return;
-  }
-
-  check_has_required_key (self,
-                          LIBRETRO_GROUP,
-                          NAME_KEY,
-                          &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return;
-  }
-
-  check_has_required_key (self,
-                          LIBRETRO_GROUP,
-                          MODULE_KEY,
-                          &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return;
-  }
-
-  check_has_required_key (self,
-                          LIBRETRO_GROUP,
-                          LIBRETRO_VERSION_KEY,
-                          &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return;
-  }
+  retro_try_propagate ({
+    check_has_required_key (self,
+                            LIBRETRO_GROUP,
+                            TYPE_KEY,
+                            &catch);
+  }, catch, error);
+
+  retro_try_propagate ({
+    check_has_required_key (self,
+                            LIBRETRO_GROUP,
+                            NAME_KEY,
+                            &catch);
+  }, catch, error);
+
+  retro_try_propagate ({
+    check_has_required_key (self,
+                            LIBRETRO_GROUP,
+                            MODULE_KEY,
+                            &catch);
+  }, catch, error);
+
+  retro_try_propagate ({
+    check_has_required_key (self,
+                            LIBRETRO_GROUP,
+                            LIBRETRO_VERSION_KEY,
+                            &catch);
+  }, catch, error);
 }
 
 static void
@@ -267,43 +244,33 @@ check_platform_group (RetroCoreDescriptor  *self,
 {
   gboolean has_key;
   g_auto (GStrv) firmwares = NULL;
-  GError *tmp_error = NULL;
 
   g_assert (group != NULL);
 
-  check_has_required_key (self,
-                          group,
-                          PLATFORM_MIME_TYPE_KEY,
-                          &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return;
-  }
+  retro_try_propagate ({
+    check_has_required_key (self,
+                            group,
+                            PLATFORM_MIME_TYPE_KEY,
+                            &catch);
+  }, catch, error);
 
-  has_key = g_key_file_has_key (self->key_file,
-                                group,
-                                PLATFORM_FIRMWARES_KEY,
-                                &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return;
-  }
+  retro_try_propagate ({
+    has_key = g_key_file_has_key (self->key_file,
+                                  group,
+                                  PLATFORM_FIRMWARES_KEY,
+                                  &catch);
+  }, catch, error);
 
   if (!has_key)
     return;
 
-  firmwares = g_key_file_get_string_list (self->key_file,
-                                          group,
-                                          PLATFORM_FIRMWARES_KEY,
-                                          NULL,
-                                          &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return;
-  }
+  retro_try_propagate ({
+    firmwares = g_key_file_get_string_list (self->key_file,
+                                            group,
+                                            PLATFORM_FIRMWARES_KEY,
+                                            NULL,
+                                            &catch);
+  }, catch, error);
 
   for (GStrv firmware_p = firmwares; *firmware_p != NULL; firmware_p++) {
     g_autofree gchar *firmware_group = NULL;
@@ -329,29 +296,21 @@ check_firmware_group (RetroCoreDescriptor  *self,
                       const gchar          *group,
                       GError              **error)
 {
-  GError *tmp_error = NULL;
-
   g_assert (group != NULL);
 
-  check_has_required_key (self,
-                          group,
-                          FIRMWARE_PATH_KEY,
-                          &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
+  retro_try_propagate ({
+    check_has_required_key (self,
+                            group,
+                            FIRMWARE_PATH_KEY,
+                            &catch);
+  }, catch, error);
 
-    return;
-  }
-
-  check_has_required_key (self,
-                          group,
-                          FIRMWARE_MANDATORY_KEY,
-                          &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return;
-  }
+  retro_try_propagate ({
+    check_has_required_key (self,
+                            group,
+                            FIRMWARE_MANDATORY_KEY,
+                            &catch);
+  }, catch, error);
 }
 
 /* Public */
@@ -370,19 +329,15 @@ retro_core_descriptor_has_icon (RetroCoreDescriptor  *self,
                                 GError              **error)
 {
   gboolean has_icon;
-  GError *tmp_error = NULL;
 
   g_return_val_if_fail (RETRO_IS_CORE_DESCRIPTOR (self), FALSE);
 
-  has_icon = g_key_file_has_key (self->key_file,
-                                 LIBRETRO_GROUP,
-                                 ICON_KEY,
-                                 &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return FALSE;
-  }
+  retro_try_propagate_val ({
+    has_icon = g_key_file_has_key (self->key_file,
+                                   LIBRETRO_GROUP,
+                                   ICON_KEY,
+                                   &catch);
+  }, catch, error, FALSE);
 
   return has_icon;
 }
@@ -440,19 +395,15 @@ retro_core_descriptor_get_is_game (RetroCoreDescriptor  *self,
 {
   g_autofree gchar *type = NULL;
   gboolean is_game;
-  GError *tmp_error = NULL;
 
   g_return_val_if_fail (RETRO_IS_CORE_DESCRIPTOR (self), FALSE);
 
-  type = g_key_file_get_string (self->key_file,
-                                LIBRETRO_GROUP,
-                                TYPE_KEY,
-                                &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return FALSE;
-  }
+  retro_try_propagate_val ({
+    type = g_key_file_get_string (self->key_file,
+                                  LIBRETRO_GROUP,
+                                  TYPE_KEY,
+                                  &catch);
+  }, catch, error, FALSE);
 
   is_game = g_strcmp0 (type, TYPE_GAME) == 0;
 
@@ -474,19 +425,15 @@ retro_core_descriptor_get_is_emulator (RetroCoreDescriptor  *self,
 {
   g_autofree gchar *type = NULL;
   gboolean is_emulator;
-  GError *tmp_error = NULL;
 
   g_return_val_if_fail (RETRO_IS_CORE_DESCRIPTOR (self), FALSE);
 
-  type = g_key_file_get_string (self->key_file,
-                                LIBRETRO_GROUP,
-                                TYPE_KEY,
-                                &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return FALSE;
-  }
+  retro_try_propagate_val ({
+    type = g_key_file_get_string (self->key_file,
+                                  LIBRETRO_GROUP,
+                                  TYPE_KEY,
+                                  &catch);
+  }, catch, error, FALSE);
 
   is_emulator = g_strcmp0 (type, TYPE_EMULATOR) == 0;
 
@@ -529,19 +476,15 @@ retro_core_descriptor_get_icon (RetroCoreDescriptor  *self,
 {
   g_autofree gchar *icon_name = NULL;
   GIcon *icon;
-  GError *tmp_error = NULL;
 
   g_return_val_if_fail (RETRO_IS_CORE_DESCRIPTOR (self), NULL);
 
-  icon_name = g_key_file_get_string (self->key_file,
-                                     LIBRETRO_GROUP,
-                                     ICON_KEY,
-                                     &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return NULL;
-  }
+  retro_try_propagate_val ({
+    icon_name = g_key_file_get_string (self->key_file,
+                                       LIBRETRO_GROUP,
+                                       ICON_KEY,
+                                       &catch);
+  }, catch, error, NULL);
 
   icon = G_ICON (g_themed_icon_new (icon_name));
 
@@ -586,7 +529,6 @@ retro_core_descriptor_get_module_file (RetroCoreDescriptor  *self,
   g_autoptr (GFile) dir = NULL;
   g_autofree gchar *module = NULL;
   g_autoptr (GFile) module_file = NULL;
-  GError *tmp_error = NULL;
 
   g_return_val_if_fail (RETRO_IS_CORE_DESCRIPTOR (self), NULL);
 
@@ -595,12 +537,9 @@ retro_core_descriptor_get_module_file (RetroCoreDescriptor  *self,
   if (dir == NULL)
     return NULL;
 
-  module = retro_core_descriptor_get_module (self, &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return NULL;
-  }
+  retro_try_propagate_val ({
+    module = retro_core_descriptor_get_module (self, &catch);
+  }, catch, error, NULL);
 
   module_file = g_file_get_child (dir, module);
   if (!g_file_query_exists (module_file, NULL))
@@ -850,21 +789,18 @@ retro_core_descriptor_get_is_firmware_mandatory (RetroCoreDescriptor  *self,
 {
   g_autofree gchar *group = NULL;
   gboolean is_mandatory;
-  GError *tmp_error = NULL;
 
   g_return_val_if_fail (RETRO_IS_CORE_DESCRIPTOR (self), FALSE);
   g_return_val_if_fail (firmware != NULL, FALSE);
 
   group = g_strconcat (FIRMWARE_GROUP_PREFIX, firmware, NULL);
-  is_mandatory = g_key_file_get_boolean (self->key_file,
-                                         group,
-                                         FIRMWARE_MANDATORY_KEY,
-                                         &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return FALSE;
-  }
+
+  retro_try_propagate_val ({
+    is_mandatory = g_key_file_get_boolean (self->key_file,
+                                           group,
+                                           FIRMWARE_MANDATORY_KEY,
+                                           &catch);
+  }, catch, error, FALSE);
 
   return is_mandatory;
 }
@@ -888,22 +824,18 @@ retro_core_descriptor_get_platform_supports_mime_types (RetroCoreDescriptor  *se
 {
   g_auto (GStrv) supported_mime_types = NULL;
   gsize supported_mime_types_length;
-  GError *tmp_error = NULL;
 
   g_return_val_if_fail (RETRO_IS_CORE_DESCRIPTOR (self), FALSE);
   g_return_val_if_fail (platform != NULL, FALSE);
   g_return_val_if_fail (mime_types != NULL, FALSE);
 
-  supported_mime_types =
-    retro_core_descriptor_get_mime_type (self,
-                                         platform,
-                                         &supported_mime_types_length,
-                                         &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return FALSE;
-  }
+  retro_try_propagate_val ({
+    supported_mime_types =
+      retro_core_descriptor_get_mime_type (self,
+                                           platform,
+                                           &supported_mime_types_length,
+                                           &catch);
+  }, catch, error, FALSE);
 
   for (; *mime_types != NULL; mime_types++)
     if (!g_strv_contains ((const char * const *) supported_mime_types,
@@ -929,49 +861,36 @@ retro_core_descriptor_new (const gchar  *filename,
   g_autoptr (RetroCoreDescriptor) self = NULL;
   g_auto (GStrv) groups = NULL;
   gsize groups_length;
-  GError *tmp_error = NULL;
 
   g_return_val_if_fail (filename != NULL, NULL);
 
   self =  g_object_new (RETRO_TYPE_CORE_DESCRIPTOR, NULL);
   self->filename = g_strdup (filename);
   self->key_file = g_key_file_new ();
-  g_key_file_load_from_file (self->key_file,
-                             filename,
-                             G_KEY_FILE_NONE,
-                             &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
+  retro_try_propagate_val ({
+    g_key_file_load_from_file (self->key_file,
+                               filename,
+                               G_KEY_FILE_NONE,
+                               &catch);
+  }, catch, error, NULL);
 
-    return NULL;
-  }
-
-  check_libretro_group (self, &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return NULL;
-  }
+  retro_try_propagate_val ({
+    check_libretro_group (self, &catch);
+  }, catch, error, NULL);
 
   groups = g_key_file_get_groups (self->key_file, &groups_length);
   for (gsize i = 0; i < groups_length; i++) {
     if (g_str_has_prefix (groups[i],
                           PLATFORM_GROUP_PREFIX)) {
-      check_platform_group (self, groups[i], &tmp_error);
-      if (G_UNLIKELY (tmp_error != NULL)) {
-        g_propagate_error (error, tmp_error);
-
-        return NULL;
-      }
+      retro_try_propagate_val ({
+        check_platform_group (self, groups[i], &catch);
+      }, catch, error, NULL);
     }
     else if (g_str_has_prefix (groups[i],
                                FIRMWARE_GROUP_PREFIX)) {
-      check_firmware_group (self, groups[i], &tmp_error);
-      if (G_UNLIKELY (tmp_error != NULL)) {
-        g_propagate_error (error, tmp_error);
-
-        return NULL;
-      }
+      retro_try_propagate_val ({
+        check_firmware_group (self, groups[i], &catch);
+      }, catch, error, NULL);
     }
   }
 
diff --git a/retro-gtk/retro-core.c b/retro-gtk/retro-core.c
index a290e15..ab3a048 100644
--- a/retro-gtk/retro-core.c
+++ b/retro-gtk/retro-core.c
@@ -17,6 +17,7 @@
 #include "retro-controller-iterator-private.h"
 #include "retro-controller-state-private.h"
 #include "retro-controller-type.h"
+#include "retro-error-private.h"
 #include "retro-framebuffer-private.h"
 #include "retro-input-private.h"
 #include "retro-keyboard-private.h"
@@ -1020,26 +1021,24 @@ insert_variable (RetroCore   *self,
                  const gchar *value)
 {
   RetroOption *option;
-  GError *tmp_error = NULL;
 
-  option = retro_option_new (key, value, &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_debug ("%s", tmp_error->message);
-    g_clear_error (&tmp_error);
+  retro_try ({
+    option = retro_option_new (key, value, &catch);
+  }, catch, {
+    g_debug ("%s", catch->message);
 
     return;
-  }
+  });
 
   if (g_hash_table_contains (self->option_overrides, key)) {
     gchar *override;
 
     override = g_hash_table_lookup (self->option_overrides, key);
-    retro_option_set_value (option, override, &tmp_error);
-
-    if (G_UNLIKELY (tmp_error != NULL)) {
-      g_debug ("%s", tmp_error->message);
-      g_clear_error (&tmp_error);
-    }
+    retro_try ({
+      retro_option_set_value (option, override, &catch);
+    }, catch, {
+      g_debug ("%s", catch->message);
+    });
   }
 
   g_hash_table_insert (self->options, g_strdup (key), option);
@@ -1193,11 +1192,12 @@ retro_core_boot (RetroCore  *self,
 
   g_return_if_fail (RETRO_IS_CORE (self));
 
-  retro_runner_process_start (self->process, &tmp_error);
-  if (tmp_error) {
-    crash (self, tmp_error);
+  retro_try ({
+    retro_runner_process_start (self->process, &catch);
+  }, catch, {
+    crash (self, catch);
     return;
-  }
+  });
 
   proxy = retro_runner_process_get_proxy (self->process);
   g_signal_connect_object (proxy, "variables-set", G_CALLBACK (variables_set_cb), self, 0);
@@ -1251,11 +1251,12 @@ retro_core_boot (RetroCore  *self,
 
   g_variant_get (framebuffer_variant, "h", &handle);
   if (G_LIKELY (handle < g_unix_fd_list_get_length (out_fd_list))) {
-    fd = g_unix_fd_list_get (out_fd_list, handle, &tmp_error);
-    if (tmp_error) {
-      crash (self, tmp_error);
+    retro_try ({
+      fd = g_unix_fd_list_get (out_fd_list, handle, &catch);
+    }, catch, {
+      crash (self, catch);
       return;
-    }
+    });
   } else {
     g_critical ("Invalid framebuffer handle");
     return;
diff --git a/retro-gtk/retro-gl-display.c b/retro-gtk/retro-gl-display.c
index 17e89c7..b58f902 100644
--- a/retro-gtk/retro-gl-display.c
+++ b/retro-gtk/retro-gl-display.c
@@ -10,6 +10,7 @@
 #include "retro-gl-display-private.h"
 
 #include <epoxy/gl.h>
+#include "retro-error-private.h"
 #include "retro-glsl-filter-private.h"
 #include "retro-pixbuf.h"
 #include "retro-pixdata.h"
@@ -209,7 +210,6 @@ realize (RetroGLDisplay *self)
   GLuint vertex_array_object;
   GLuint element_buffer_object;
   RetroVideoFilter current_filter;
-  GError *inner_error = NULL;
 
   gtk_gl_area_make_current (GTK_GL_AREA (self));
 
@@ -227,16 +227,16 @@ realize (RetroGLDisplay *self)
   for (RetroVideoFilter filter = 0; filter < RETRO_VIDEO_FILTER_COUNT; filter++) {
     RetroGLSLShader *shader;
 
-    self->glsl_filter[filter] = retro_glsl_filter_new (filter_uris[filter], &inner_error);
-    if (G_UNLIKELY (inner_error != NULL)) {
+    retro_try ({
+      self->glsl_filter[filter] = retro_glsl_filter_new (filter_uris[filter], &catch);
+    }, catch, {
       g_critical ("Shader program %s creation failed: %s",
                   filter_uris[filter],
-                  inner_error->message);
+                  catch->message);
       g_clear_object (&self->glsl_filter[filter]);
-      g_clear_error (&inner_error);
 
       continue;
-    }
+    });
 
     shader = retro_glsl_filter_get_shader (self->glsl_filter[filter]);
 
diff --git a/retro-gtk/retro-glsl-filter.c b/retro-gtk/retro-glsl-filter.c
index d4c0d8f..d3f564e 100644
--- a/retro-gtk/retro-glsl-filter.c
+++ b/retro-gtk/retro-glsl-filter.c
@@ -2,6 +2,8 @@
 
 #include "retro-glsl-filter-private.h"
 
+#include "retro-error-private.h"
+
 struct _RetroGLSLFilter
 {
   GObject parent_instance;
@@ -18,15 +20,14 @@ g_key_file_try_get_string (GKeyFile    *key_file,
                            const gchar *key)
 {
   const gchar *value;
-  GError *inner_error = NULL;
 
-  value = g_key_file_get_string (key_file, group, key, &inner_error);
-  if (G_UNLIKELY (inner_error != NULL)) {
-    g_debug ("%s", inner_error->message);
-    g_clear_error (&inner_error);
+  retro_try ({
+    value = g_key_file_get_string (key_file, group, key, &catch);
+  }, catch, {
+    g_debug ("%s", catch->message);
 
     return NULL;
-  }
+  });
 
   return value;
 }
@@ -37,41 +38,40 @@ g_file_try_read_bytes (GFile *file)
   g_autoptr (GFileInputStream) stream = NULL;
   goffset size;
   GBytes *bytes;
-  GError *inner_error = NULL;
 
-  stream = g_file_read (file, NULL, &inner_error);
-  if (G_UNLIKELY (inner_error != NULL)) {
-    g_debug ("%s", inner_error->message);
-    g_clear_error (&inner_error);
+  retro_try ({
+    stream = g_file_read (file, NULL, &catch);
+  }, catch, {
+    g_debug ("%s", catch->message);
 
     return NULL;
-  }
+  });
 
-  g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_END, NULL, &inner_error);
-  if (G_UNLIKELY (inner_error != NULL)) {
-    g_debug ("%s", inner_error->message);
-    g_clear_error (&inner_error);
+  retro_try ({
+    g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_END, NULL, &catch);
+  }, catch, {
+    g_debug ("%s", catch->message);
 
     return NULL;
-  }
+  });
 
   size = g_seekable_tell (G_SEEKABLE (stream));
 
-  g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, &inner_error);
-  if (G_UNLIKELY (inner_error != NULL)) {
-    g_debug ("%s", inner_error->message);
-    g_clear_error (&inner_error);
+  retro_try ({
+    g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, &catch);
+  }, catch, {
+    g_debug ("%s", catch->message);
 
     return NULL;
-  }
+  });
 
-  bytes = g_input_stream_read_bytes (G_INPUT_STREAM (stream), size, NULL, &inner_error);
-  if (G_UNLIKELY (inner_error != NULL)) {
-    g_debug ("%s", inner_error->message);
-    g_clear_error (&inner_error);
+  retro_try ({
+    bytes = g_input_stream_read_bytes (G_INPUT_STREAM (stream), size, NULL, &catch);
+  }, catch, {
+    g_debug ("%s", catch->message);
 
     return NULL;
-  }
+  });
 
   return bytes;
 }
@@ -119,7 +119,6 @@ retro_glsl_filter_new (const char  *uri,
   g_autoptr (GFile) parent = NULL;
   g_autoptr (GBytes) bytes = NULL;
   const gchar *value;
-  GError *inner_error = NULL;
 
   g_return_val_if_fail (uri != NULL, NULL);
 
@@ -129,12 +128,9 @@ retro_glsl_filter_new (const char  *uri,
     return NULL;
 
   key_file = g_key_file_new ();
-  g_key_file_load_from_bytes (key_file, bytes, G_KEY_FILE_NONE, &inner_error);
-  if (G_UNLIKELY (inner_error != NULL)) {
-    g_propagate_error (error, inner_error);
-
-    return NULL;
-  }
+  retro_try_propagate_val ({
+    g_key_file_load_from_bytes (key_file, bytes, G_KEY_FILE_NONE, &catch);
+  }, catch, error, NULL);
 
   self = g_object_new (RETRO_TYPE_GLSL_FILTER, NULL);
 
@@ -172,16 +168,13 @@ retro_glsl_filter_new (const char  *uri,
   if (fragment == NULL)
     fragment = g_file_try_read_child_bytes (parent, "sharp.fs");
 
-  self->shader = retro_glsl_shader_new (vertex,
-                                        fragment,
-                                        wrap,
-                                        filter,
-                                        &inner_error);
-  if (G_UNLIKELY (inner_error != NULL)) {
-    g_propagate_error (error, inner_error);
-
-    return NULL;
-  }
+  retro_try_propagate_val ({
+    self->shader = retro_glsl_shader_new (vertex,
+                                          fragment,
+                                          wrap,
+                                          filter,
+                                          &catch);
+  }, catch, error, NULL);
 
   return g_steal_pointer (&self);
 }
diff --git a/retro-gtk/retro-glsl-shader.c b/retro-gtk/retro-glsl-shader.c
index 51eb148..b606c1b 100644
--- a/retro-gtk/retro-glsl-shader.c
+++ b/retro-gtk/retro-glsl-shader.c
@@ -2,6 +2,8 @@
 
 #include "retro-glsl-shader-private.h"
 
+#include "retro-error-private.h"
+
 struct _RetroGLSLShader
 {
   GObject parent_instance;
@@ -98,7 +100,6 @@ retro_glsl_shader_new (GBytes  *vertex,
   gint status = 0;
   GLuint vertex_shader;
   GLuint fragment_shader;
-  GError *inner_error = NULL;
 
   g_return_val_if_fail (vertex != NULL, NULL);
   g_return_val_if_fail (fragment != NULL, NULL);
@@ -112,20 +113,17 @@ retro_glsl_shader_new (GBytes  *vertex,
   self->wrap = wrap;
   self->filter = filter;
 
-  vertex_shader = create_shader (self->vertex, GL_VERTEX_SHADER, &inner_error);
-  if (G_UNLIKELY (inner_error != NULL)) {
-    g_propagate_error (error, inner_error);
+  retro_try_propagate_val ({
+    vertex_shader = create_shader (self->vertex, GL_VERTEX_SHADER, &catch);
+  }, catch, error, NULL);
 
-    return NULL;
-  }
-
-  fragment_shader = create_shader (self->fragment, GL_FRAGMENT_SHADER, &inner_error);
-  if (G_UNLIKELY (inner_error != NULL)) {
-    g_propagate_error (error, inner_error);
+  retro_try ({
+    fragment_shader = create_shader (self->fragment, GL_FRAGMENT_SHADER, &catch);
+  }, catch, {
     glDeleteShader (vertex_shader);
 
     return NULL;
-  }
+  });
 
   self->program = glCreateProgram ();
   glAttachShader (self->program, vertex_shader);
diff --git a/retro-gtk/retro-module-iterator.c b/retro-gtk/retro-module-iterator.c
index 939ac4a..701909f 100644
--- a/retro-gtk/retro-module-iterator.c
+++ b/retro-gtk/retro-module-iterator.c
@@ -9,6 +9,8 @@
 
 #include "retro-module-iterator.h"
 
+#include "retro-error-private.h"
+
 struct _RetroModuleIterator
 {
   GObject parent_instance;
@@ -120,7 +122,6 @@ iterate_next_in_current_path (RetroModuleIterator  *self,
   g_autoptr (GFile) core_descriptor_file = NULL;
   g_autofree gchar *core_descriptor_path = NULL;
   RetroCoreDescriptor *core_descriptor;
-  GError *tmp_error = NULL;
 
   g_assert (G_IS_FILE (directory));
   g_assert (G_IS_FILE_INFO (info));
@@ -150,14 +151,13 @@ iterate_next_in_current_path (RetroModuleIterator  *self,
 
   core_descriptor_file = g_file_get_child (directory, core_descriptor_basename);
   core_descriptor_path = g_file_get_path (core_descriptor_file);
-  core_descriptor = retro_core_descriptor_new (core_descriptor_path, &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_debug ("%s", tmp_error->message);
-
-    g_error_free (tmp_error);
+  retro_try ({
+    core_descriptor = retro_core_descriptor_new (core_descriptor_path, &catch);
+  }, catch, {
+    g_debug ("%s", catch->message);
 
     return FALSE;
-  }
+  });
 
   g_clear_object (&self->core_descriptor);
   self->core_descriptor = core_descriptor;
@@ -171,7 +171,6 @@ next_in_current_path (RetroModuleIterator  *self,
 {
   g_autoptr (GFile) directory = NULL;
   gboolean found = FALSE;
-  GError *tmp_error = NULL;
 
   if (self->sub_directory != NULL && next_in_sub_directory (self))
     return TRUE;
@@ -179,18 +178,18 @@ next_in_current_path (RetroModuleIterator  *self,
   directory = g_file_new_for_path (self->directories[self->current_directory]);
 
   if (self->file_enumerator == NULL) {
-    self->file_enumerator =
-      g_file_enumerate_children (directory,
-                                 "",
-                                 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                 NULL,
-                                 &tmp_error);
-    if (G_UNLIKELY (tmp_error != NULL)) {
-      g_propagate_error (error, tmp_error);
-      g_clear_object (&self->file_enumerator);
-
-      return FALSE;
-    }
+    g_autoptr (GFileEnumerator) file_enumerator = NULL;
+
+    retro_try_propagate_val ({
+      file_enumerator =
+        g_file_enumerate_children (directory,
+                                   "",
+                                   G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                   NULL,
+                                   &catch);
+    }, catch, error, FALSE);
+
+    self->file_enumerator = g_steal_pointer (&file_enumerator);
   }
 
   if (self->file_enumerator == NULL)
@@ -199,22 +198,16 @@ next_in_current_path (RetroModuleIterator  *self,
   while (TRUE) {
     g_autoptr (GFileInfo) info = NULL;
 
-    info = g_file_enumerator_next_file (self->file_enumerator, NULL, &tmp_error);
-    if (G_UNLIKELY (tmp_error != NULL)) {
-      g_propagate_error (error, tmp_error);
-
-      return FALSE;
-    }
+    retro_try_propagate_val ({
+      info = g_file_enumerator_next_file (self->file_enumerator, NULL, &catch);
+    }, catch, error, FALSE);
 
     if (info == NULL)
       break;
 
-    found = iterate_next_in_current_path (self, directory, info, &tmp_error);
-    if (G_UNLIKELY (tmp_error != NULL)) {
-      g_propagate_error (error, tmp_error);
-
-      return FALSE;
-    }
+    retro_try_propagate_val ({
+      found = iterate_next_in_current_path (self, directory, info, &catch);
+    }, catch, error, FALSE);
 
     if (found)
       return TRUE;
@@ -258,19 +251,18 @@ gboolean
 retro_module_iterator_next (RetroModuleIterator *self)
 {
   gboolean found_next_in_current_path;
-  GError *tmp_error = NULL;
 
   g_return_val_if_fail (RETRO_IS_MODULE_ITERATOR (self), FALSE);
 
   while (self->directories[self->current_directory] != NULL) {
     set_current_directory_as_visited (self);
 
-    found_next_in_current_path = next_in_current_path (self, &tmp_error);
-    if (G_UNLIKELY (tmp_error != NULL)) {
-      g_debug ("%s", tmp_error->message);
-      g_clear_error (&tmp_error);
+    retro_try ({
+      found_next_in_current_path = next_in_current_path (self, &catch);
+    }, catch, {
+      g_debug ("%s", catch->message);
       found_next_in_current_path = FALSE;
-    }
+    });
 
     if (found_next_in_current_path)
       return TRUE;
diff --git a/retro-gtk/retro-runner-process.c b/retro-gtk/retro-runner-process.c
index 953ad69..490ddcb 100644
--- a/retro-gtk/retro-runner-process.c
+++ b/retro-gtk/retro-runner-process.c
@@ -7,6 +7,7 @@
 #include <glib-unix.h>
 #include <gio/gunixconnection.h>
 #include <sys/socket.h>
+#include "retro-error-private.h"
 
 struct _RetroRunnerProcess
 {
@@ -233,7 +234,6 @@ retro_runner_process_start (RetroRunnerProcess  *self,
   g_autoptr(GSocketConnection) connection = NULL;
   g_autoptr(GSubprocessLauncher) launcher = NULL;
   g_autoptr(GSubprocess) process = NULL;
-  GError *tmp_error = NULL;
 
   g_return_if_fail (RETRO_IS_RUNNER_PROCESS (self));
   g_return_if_fail (!G_IS_DBUS_CONNECTION (self->connection));
@@ -243,22 +243,20 @@ retro_runner_process_start (RetroRunnerProcess  *self,
   if (!(connection = create_connection (launcher, 3, error)))
     return;
 
-  if (!(process = g_subprocess_launcher_spawn (launcher, &tmp_error,
+  retro_try_propagate ({
+    process = g_subprocess_launcher_spawn (launcher, &catch,
                                                RETRO_RUNNER_PATH,
                                                g_get_application_name (),
-                                               self->filename, NULL))) {
-    g_propagate_error (error, tmp_error);
-    return;
-  }
+                                               self->filename, NULL);
+  }, catch, error);
 
-  if (!(self->connection = g_dbus_connection_new_sync (G_IO_STREAM (connection),
+  retro_try_propagate ({
+    self->connection = g_dbus_connection_new_sync (G_IO_STREAM (connection),
                                                        NULL,
                                                        G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING |
                                                        G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
-                                                       NULL, NULL, &tmp_error))) {
-    g_propagate_error (error, tmp_error);
-    return;
-  }
+                                                       NULL, NULL, &catch);
+  }, catch, error);
 
   g_dbus_connection_start_message_processing (self->connection);
 
@@ -266,11 +264,11 @@ retro_runner_process_start (RetroRunnerProcess  *self,
   g_subprocess_wait_check_async (process, self->cancellable,
                                  (GAsyncReadyCallback) wait_check_cb, self);
 
-  self->proxy = ipc_runner_proxy_new_sync (self->connection, 0, NULL,
-                                           "/org/gnome/Retro/Runner", NULL,
-                                           &tmp_error);
-  if (!self->proxy)
-    g_propagate_error (error, tmp_error);
+  retro_try_propagate ({
+    self->proxy = ipc_runner_proxy_new_sync (self->connection, 0, NULL,
+                                             "/org/gnome/Retro/Runner", NULL,
+                                             &catch);
+  }, catch, error);
 }
 
 /**
@@ -284,15 +282,14 @@ void
 retro_runner_process_stop (RetroRunnerProcess  *self,
                            GError             **error)
 {
-  GError *tmp_error = NULL;
-
   g_return_if_fail (RETRO_IS_RUNNER_PROCESS (self));
   g_return_if_fail (G_IS_DBUS_CONNECTION (self->connection));
 
   g_cancellable_cancel (self->cancellable);
 
-  if (!g_dbus_connection_close_sync (self->connection, NULL, &tmp_error))
-    g_propagate_error (error, tmp_error);
+  retro_try_propagate ({
+    g_dbus_connection_close_sync (self->connection, NULL, &catch);
+  }, catch, error);
 
   g_clear_object (&self->proxy);
   g_clear_object (&self->connection);
diff --git a/retro-runner/retro-core.c b/retro-runner/retro-core.c
index 784b182..d4a6e3d 100644
--- a/retro-runner/retro-core.c
+++ b/retro-runner/retro-core.c
@@ -4,6 +4,7 @@
 
 #include <gio/gio.h>
 #include <string.h>
+#include "retro-error-private.h"
 #include "retro-input-private.h"
 #include "retro-main-loop-source-private.h"
 #include "retro-memfd-private.h"
@@ -842,57 +843,38 @@ load_discs (RetroCore  *self,
 {
   guint length;
   gboolean fullpath;
-  GError *tmp_error = NULL;
 
-  set_disk_ejected (self, TRUE, &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return;
-  }
+  retro_try_propagate ({
+    set_disk_ejected (self, TRUE, &catch);
+  }, catch, error);
 
   length = g_strv_length (self->media_uris);
-  while (get_disk_images_number (self, &tmp_error) < length &&
-         (tmp_error == NULL)) {
-   add_disk_image_index (self, &tmp_error);
-    if (G_UNLIKELY (tmp_error != NULL)) {
-      g_propagate_error (error, tmp_error);
-
-      return;
+  retro_try_propagate ({
+    while (get_disk_images_number (self, &catch) < length && (catch == NULL)) {
+      retro_try_propagate ({
+        add_disk_image_index (self, &catch);
+      }, catch, error);
     }
-  }
-
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return;
-  }
+  }, catch, error);
 
   fullpath = get_needs_full_path (self);
   for (gsize index = 0; index < length; index++) {
     g_autoptr (RetroGameInfo) game_info = NULL;
 
-    game_info = retro_game_info_new (self->media_uris[index], fullpath, &tmp_error);
-    if (G_UNLIKELY (tmp_error != NULL)) {
-      g_propagate_error (error, tmp_error);
-
-      return;
-    }
-
-    replace_disk_image_index (self, index, game_info, &tmp_error);
-    if (G_UNLIKELY (tmp_error != NULL)) {
-      g_propagate_error (error, tmp_error);
+    retro_try_propagate ({
+      game_info = retro_game_info_new (self->media_uris[index],
+                                       fullpath,
+                                       &catch);
+    }, catch, error);
 
-      return;
-    }
+    retro_try_propagate ({
+      replace_disk_image_index (self, index, game_info, &catch);
+    }, catch, error);
   }
 
-  set_disk_ejected (self, FALSE, &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return;
-  }
+  retro_try_propagate ({
+    set_disk_ejected (self, FALSE, &catch);
+  }, catch, error);
 }
 
 static gboolean
@@ -949,7 +931,6 @@ load_medias (RetroCore  *self,
 {
   guint length;
   g_autoptr (RetroGameInfo) game_info = NULL;
-  GError *tmp_error = NULL;
 
   length = self->media_uris == NULL ? 0 : g_strv_length (self->media_uris);
 
@@ -959,24 +940,19 @@ load_medias (RetroCore  *self,
     return;
   }
 
-  game_info = retro_game_info_new (self->media_uris[0], get_needs_full_path (self), &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return;
-  }
+  retro_try_propagate ({
+    game_info = retro_game_info_new (self->media_uris[0],
+                                     get_needs_full_path (self),
+                                     &catch);
+  }, catch, error);
 
   if (!load_game (self, game_info))
     return;
 
-  if (self->disk_control_callback != NULL) {
-    load_discs (self, &tmp_error);
-    if (G_UNLIKELY (tmp_error != NULL)) {
-      g_propagate_error (error, tmp_error);
-
-      return;
-    }
-  }
+  if (self->disk_control_callback != NULL)
+    retro_try_propagate ({
+      load_discs (self, &catch);
+    }, catch, error);
 }
 
 void retro_core_set_environment_interface (RetroCore *self);
@@ -1334,7 +1310,6 @@ retro_core_boot (RetroCore  *self,
                  GError    **error)
 {
   RetroInit init;
-  GError *tmp_error = NULL;
 
   g_return_if_fail (RETRO_IS_CORE (self));
 
@@ -1345,12 +1320,9 @@ retro_core_boot (RetroCore  *self,
 
   set_is_initiated (self, TRUE);
 
-  load_medias (self, &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return;
-  }
+  retro_try_propagate ({
+    load_medias (self, &catch);
+  }, catch, error);
 }
 
 /**
@@ -1390,7 +1362,6 @@ retro_core_set_current_media (RetroCore  *self,
                               GError    **error)
 {
   guint length;
-  GError *tmp_error = NULL;
 
   g_return_if_fail (RETRO_IS_CORE (self));
 
@@ -1401,26 +1372,17 @@ retro_core_set_current_media (RetroCore  *self,
   if (self->disk_control_callback == NULL)
     return;
 
-  set_disk_ejected (self, TRUE, &tmp_error);
-  if (tmp_error != NULL) {
-    g_propagate_error (error, tmp_error);
-
-    return;
-  }
-
-  set_disk_image_index (self, media_index, &tmp_error);
-  if (tmp_error != NULL) {
-    g_propagate_error (error, tmp_error);
-
-    return;
-  }
+  retro_try_propagate ({
+    set_disk_ejected (self, TRUE, &catch);
+  }, catch, error);
 
-  set_disk_ejected (self, FALSE, &tmp_error);
-  if (tmp_error != NULL) {
-    g_propagate_error (error, tmp_error);
+  retro_try_propagate ({
+    set_disk_image_index (self, media_index, &catch);
+  }, catch, error);
 
-    return;
-  }
+  retro_try_propagate ({
+    set_disk_ejected (self, FALSE, &catch);
+  }, catch, error);
 }
 
 void
@@ -1701,7 +1663,6 @@ retro_core_save_state (RetroCore    *self,
   g_autofree guint8 *data = NULL;
   gsize size;
   gboolean success;
-  g_autoptr (GError) tmp_error = NULL;
 
   g_return_if_fail (RETRO_IS_CORE (self));
   g_return_if_fail (filename != NULL);
@@ -1732,12 +1693,16 @@ retro_core_save_state (RetroCore    *self,
     return;
   }
 
-  g_file_set_contents (filename, (gchar *) data, size, &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL))
+  retro_try ({
+    g_file_set_contents (filename, (gchar *) data, size, &catch);
+  }, catch, {
     g_set_error (error,
                  RETRO_CORE_ERROR,
                  RETRO_CORE_ERROR_COULDNT_ACCESS_FILE,
-                 "Couldn't serialize the internal state: %s", tmp_error->message);
+                 "Couldn't serialize the internal state: %s", catch->message);
+
+    return;
+  });
 }
 
 /**
@@ -1758,20 +1723,20 @@ retro_core_load_state (RetroCore    *self,
   gsize expected_size, data_size;
   g_autofree gchar *data = NULL;
   gboolean success;
-  g_autoptr (GError) tmp_error = NULL;
 
   g_return_if_fail (RETRO_IS_CORE (self));
   g_return_if_fail (filename != NULL);
 
-  g_file_get_contents (filename, &data, &data_size, &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
+  retro_try ({
+    g_file_get_contents (filename, &data, &data_size, &catch);
+  }, catch, {
     g_set_error (error,
                  RETRO_CORE_ERROR,
                  RETRO_CORE_ERROR_COULDNT_ACCESS_FILE,
-                 "Couldn't deserialize the internal state: %s", tmp_error->message);
+                 "Couldn't deserialize the internal state: %s", catch->message);
 
     return;
-  }
+  });
 
   /* Some cores, such as MAME and ParaLLEl N64, can only properly restore the
    * state after at least one frame has been run. */
@@ -1858,7 +1823,6 @@ retro_core_save_memory (RetroCore        *self,
   RetroGetMemorySize get_mem_size;
   gchar *data;
   gsize size;
-  g_autoptr (GError) tmp_error = NULL;
 
   g_return_if_fail (RETRO_IS_CORE (self));
   g_return_if_fail (filename != NULL);
@@ -1868,12 +1832,16 @@ retro_core_save_memory (RetroCore        *self,
   data = get_mem_data (memory_type);
   size = get_mem_size (memory_type);
 
-  g_file_set_contents (filename, data, size, &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL))
+  retro_try ({
+    g_file_set_contents (filename, data, size, &catch);
+  }, catch, {
     g_set_error (error,
                  RETRO_CORE_ERROR,
                  RETRO_CORE_ERROR_COULDNT_ACCESS_FILE,
-                 "Couldn't save the memory state: %s", tmp_error->message);
+                 "Couldn't save the memory state: %s", catch->message);
+
+    return;
+  });
 }
 
 /**
@@ -1897,7 +1865,6 @@ retro_core_load_memory (RetroCore        *self,
   gsize memory_region_size;
   g_autofree gchar *data = NULL;
   gsize data_size;
-  GError *tmp_error = NULL;
 
   g_return_if_fail (RETRO_IS_CORE (self));
   g_return_if_fail (filename != NULL);
@@ -1907,15 +1874,16 @@ retro_core_load_memory (RetroCore        *self,
   memory_region = get_mem_region (memory_type);
   memory_region_size = get_mem_region_size (memory_type);
 
-  g_file_get_contents (filename, &data, &data_size, &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
+  retro_try ({
+    g_file_get_contents (filename, &data, &data_size, &catch);
+  }, catch, {
     g_set_error (error,
                  RETRO_CORE_ERROR,
                  RETRO_CORE_ERROR_COULDNT_ACCESS_FILE,
-                 "Couldn't load the memory state: %s", tmp_error->message);
+                 "Couldn't load the memory state: %s", catch->message);
 
     return;
-  }
+  });
 
   if (memory_region == NULL) {
     g_set_error (error,
diff --git a/retro-runner/retro-game-info.c b/retro-runner/retro-game-info.c
index f5c84ec..80f8c12 100644
--- a/retro-runner/retro-game-info.c
+++ b/retro-runner/retro-game-info.c
@@ -3,6 +3,7 @@
 #include "retro-game-info-private.h"
 
 #include <gio/gio.h>
+#include "retro-error-private.h"
 
 G_DEFINE_BOXED_TYPE (RetroGameInfo, retro_game_info, retro_game_info_copy, retro_game_info_free)
 
@@ -22,16 +23,10 @@ retro_game_info_new (const gchar  *uri,
   self->path = g_file_get_path (file);
   if (needs_full_path)
     self->data = g_new0 (guint8, 0);
-  else {
-    GError *tmp_error = NULL;
-
-    g_file_get_contents (self->path, (gchar **) &self->data, &self->size, &tmp_error);
-    if (G_UNLIKELY (tmp_error != NULL)) {
-      g_propagate_error (error, tmp_error);
-
-      return NULL;
-    }
-  }
+  else
+    retro_try_propagate_val ({
+      g_file_get_contents (self->path, (gchar **) &self->data, &self->size, &catch);
+    }, catch, error, NULL);
 
   return g_steal_pointer (&self);
 }
diff --git a/shared/retro-error-private.h b/shared/retro-error-private.h
new file mode 100644
index 0000000..d15c548
--- /dev/null
+++ b/shared/retro-error-private.h
@@ -0,0 +1,256 @@
+// This file is part of retro-gtk. License: GPL-3.0+.
+
+#pragma once
+
+#if !defined(__RETRO_GTK_INSIDE__) && !defined(RETRO_GTK_COMPILATION)
+# error "Only <retro-gtk.h> can be included directly."
+#endif
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/**
+ * retro_try:
+ * @try: the block that can throw an error
+ * @catch: the name for the variable storing the caught error
+ * @code: the block that can handle the caught error
+ *
+ * Creates the temporary #GError pointer named @catch, executes the @try block,
+ * then checks if an error has been caught.
+ *
+ * If an error is caught, executes the @code clock and free the caught error.
+ * If you don't want the caught error to be freed, set @caught to %NULL.
+ *
+ * If you want to propagate the caught error, consider using
+ * retro_try_propagate() and retro_try_propagate_val() instead.
+ *
+ * |[<!-- language="C" -->
+ * void
+ * my_function_that_can_fail (GError **error)
+ * {
+ *   g_return_if_fail (error == NULL || *error == NULL);
+ *
+ *   // Try sub_function_that_can_fail(), if it throws an error, catch it, let
+ *   // you handle it in the second block, and frees it.
+ *   retro_try ({
+ *     sub_function_that_can_fail (&catch);
+ *   }, catch, {
+ *     g_debug ("Caught error: %s", catch->message);
+ *   });
+ *
+ *   …
+ * }
+ * ]|
+ *
+ * Error pileups are always a bug.
+ * For example, this code is incorrect:
+ * |[<!-- language="C" -->
+ * void
+ * my_function_that_can_fail (GError **error)
+ * {
+ *   g_return_if_fail (error == NULL || *error == NULL);
+ *
+ *   // Errors thrown by sub_function_that_can_fail() are incorrectly unhandled.
+ *   retro_try_propagate ({
+ *     sub_function_that_can_fail (&catch);
+ *     other_function_that_can_fail (&catch);
+ *   }, catch, {
+ *     g_debug ("Caught error: %s", catch->message);
+ *   });
+ *
+ *   …
+ * }
+ * ]|
+ *
+ * You must never return from the @try block or use statements to jump out of
+ * the @try block, that would skip the error handling, defeating the purpose of
+ * using retro_try().
+ * For example, this code is incorrect:
+ * |[<!-- language="C" -->
+ * gboolean
+ * my_function_that_can_fail (GError **error)
+ * {
+ *   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ *
+ *   // Errors thrown by sub_function_that_can_fail() are incorrectly unhandled.
+ *   retro_try ({
+ *     return sub_function_that_can_fail (&catch);
+ *   }, catch, {
+ *     g_debug ("Caught error: %s", catch->message);
+ *     return FALSE;
+ *   });
+ *
+ *   …
+ * }
+ * ]|
+ */
+#define retro_try(try, catch, code) \
+  G_STMT_START { \
+    g_autoptr (GError) catch = NULL; \
+    {try;} \
+    if (G_UNLIKELY (catch != NULL)) {code;} \
+  } G_STMT_END
+
+/**
+ * retro_try_propagate:
+ * @try: the block that can throw an error
+ * @catch: the name for the variable storing the caught error
+ * @dest: (out callee-allocates) (optional) (nullable): error return location
+ *
+ * Creates the temporary #GError pointer named @catch, executes the @try block,
+ * then checks if an error has been caught.
+ * If the function returns a value, use retro_try_propagate_val() instead.
+ *
+ * If an error is caught, it is propagated into @dest and the function returns.
+ * Otherwise, the execution continues after this block.
+ *
+ * The error variable @dest points to must be %NULL.
+ *
+ * You can't clean up before the function returns, use auto cleanups or
+ * retro_try() for that.
+ *
+ * |[<!-- language="C" -->
+ * void
+ * my_function_that_can_fail (GError **error)
+ * {
+ *   g_autofree gchar *string = NULL;
+ *
+ *   g_return_val_if_fail (error == NULL || *error == NULL);
+ *
+ *   string = g_strdup ("I will be freed on error propagation.");
+ *
+ *   // Try sub_function_that_can_fail(), if it throws an error, propagate it to
+ *   // error if it isn't NULL, frees it otherwise, and return.
+ *   retro_try_propagate ({
+ *     sub_function_that_can_fail (&catch);
+ *   }, catch, error);
+ *
+ *   // If no error was caught, continue.
+ *   …
+ * }
+ * ]|
+ *
+ * Error pileups are always a bug.
+ * For example, this code is incorrect:
+ * |[<!-- language="C" -->
+ * void
+ * my_function_that_can_fail (GError **error)
+ * {
+ *   g_return_if_fail (error == NULL || *error == NULL);
+ *
+ *   // Errors thrown by sub_function_that_can_fail() are incorrectly unhandled.
+ *   retro_try_propagate ({
+ *     sub_function_that_can_fail (&catch);
+ *     other_function_that_can_fail (&catch);
+ *   }, catch, error);
+ *
+ *   …
+ * }
+ * ]|
+ *
+ * You must never return from the @try block or use statements to jump out of
+ * the @try block, that would skip the error handling, defeating the purpose of
+ * using retro_try_propagate().
+ * For example, this code is incorrect:
+ * |[<!-- language="C" -->
+ * void
+ * my_function_that_can_fail (GError **error)
+ * {
+ *   g_return_if_fail (error == NULL || *error == NULL);
+ *
+ *   // Errors thrown by sub_function_that_can_fail() are incorrectly unhandled.
+ *   retro_try_propagate ({
+ *     if (!sub_function_that_can_fail (&catch))
+ *       return;
+ *   }, catch, error);
+ *
+ *   …
+ * }
+ * ]|
+ */
+#define retro_try_propagate(try, catch, dest) \
+  retro_try (try, catch, { g_propagate_error (dest, catch); return; })
+
+/**
+ * retro_try_propagate_val:
+ * @try: the block that can throw an error
+ * @catch: the name for the variable storing the caught error
+ * @dest: (out callee-allocates) (optional) (nullable): error return location
+ * @val: the value to return from the current function if the error is propagated
+ *
+ * Creates the temporary #GError pointer named @catch, executes the @try block,
+ * then checks if an error has been caught.
+ * If the function does not return a value, use retro_try_propagate() instead.
+ *
+ * If an error is caught, it is propagated into @dest and the function returns
+ * @val.
+ * Otherwise, the execution continues after this block.
+ *
+ * The error variable @dest points to must be %NULL.
+ *
+ * You can't clean up before the function returns, use auto cleanups or
+ * retro_try() for that.
+ *
+ * |[<!-- language="C" -->
+ * gboolean
+ * my_function_that_can_fail (GError **error)
+ * {
+ *   g_autofree gchar *string = NULL;
+ *
+ *   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ *
+ *   string = g_strdup ("I will be freed on error propagation.");
+ *
+ *   // Try sub_function_that_can_fail(), if it throws an error, propagate it to
+ *   // error if it isn't NULL, frees it otherwise, and return.
+ *   retro_try_propagate_val ({
+ *     sub_function_that_can_fail (&catch);
+ *   }, catch, error, FALSE);
+ *
+ *   // If no error was caught, continue.
+ *   …
+ * }
+ * ]|
+ *
+ * Error pileups are always a bug.
+ * For example, this code is incorrect:
+ * |[<!-- language="C" -->
+ * gboolean
+ * my_function_that_can_fail (GError **error)
+ * {
+ *   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ *
+ *   // Errors thrown by sub_function_that_can_fail() are incorrectly unhandled.
+ *   retro_try_propagate_val ({
+ *     sub_function_that_can_fail (&catch);
+ *     other_function_that_can_fail (&catch);
+ *   }, catch, error, FALSE);
+ *
+ *   …
+ * }
+ * ]|
+ *
+ * You must never return from the @try block or use statements to jump out of
+ * the @try block, that would skip the error handling, defeating the purpose of
+ * using retro_try_propagate_val().
+ * For example, this code is incorrect:
+ * |[<!-- language="C" -->
+ * gboolean
+ * my_function_that_can_fail (GError **error)
+ * {
+ *   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ *
+ *   // Errors thrown by sub_function_that_can_fail() are incorrectly unhandled.
+ *   retro_try_propagate_val ({
+ *     return sub_function_that_can_fail (&catch);
+ *   }, catch, error, FALSE);
+ *
+ *   …
+ * }
+ * ]|
+ */
+#define retro_try_propagate_val(try, catch, dest, val) \
+  retro_try (try, catch, { g_propagate_error (dest, catch); return (val); })
+
+G_END_DECLS
diff --git a/tests/retro-reftest-file.c b/tests/retro-reftest-file.c
index 0ce160c..1467d09 100644
--- a/tests/retro-reftest-file.c
+++ b/tests/retro-reftest-file.c
@@ -22,6 +22,7 @@
 
 #include <errno.h>
 #include "retro-test-controller.h"
+#include "retro-error-private.h"
 
 struct _RetroReftestFile
 {
@@ -356,28 +357,25 @@ retro_reftest_file_get_core (RetroReftestFile  *self,
   g_auto (GStrv) key_file_medias = NULL;
   gsize key_file_medias_length = 0;
   g_auto (GStrv) media_uris = NULL;
-  GError *tmp_error = NULL;
 
-  key_file_core = g_key_file_get_string (self->key_file,
-                                         RETRO_REFTEST_FILE_RETRO_REFTEST_GROUP,
-                                         RETRO_REFTEST_FILE_CORE_KEY,
-                                         &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return NULL;
-  }
+  retro_try_propagate_val ({
+    key_file_core = g_key_file_get_string (self->key_file,
+                                           RETRO_REFTEST_FILE_RETRO_REFTEST_GROUP,
+                                           RETRO_REFTEST_FILE_CORE_KEY,
+                                           &catch);
+  }, catch, error, NULL);
 
   core_file = get_sibling (self, key_file_core);
   path = g_file_get_path (core_file);
   core = retro_core_new (path);
 
-  key_file_medias = g_key_file_get_string_list (self->key_file,
-                                                RETRO_REFTEST_FILE_RETRO_REFTEST_GROUP,
-                                                RETRO_REFTEST_FILE_MEDIAS_KEY,
-                                                &key_file_medias_length,
-                                                &tmp_error);
-  g_clear_error (&tmp_error);
+  retro_try ({
+    key_file_medias = g_key_file_get_string_list (self->key_file,
+                                                  RETRO_REFTEST_FILE_RETRO_REFTEST_GROUP,
+                                                  RETRO_REFTEST_FILE_MEDIAS_KEY,
+                                                  &key_file_medias_length,
+                                                  &catch);
+  }, catch, {});
 
   if (key_file_medias == NULL)
     return core;
@@ -411,32 +409,25 @@ retro_reftest_file_get_options (RetroReftestFile  *self,
   g_auto (GStrv) keys = NULL;
   gsize keys_length = 0;
   g_autoptr (GHashTable) options = NULL;
-  GError *tmp_error = NULL;
 
-  keys = g_key_file_get_keys (self->key_file,
-                              RETRO_REFTEST_FILE_OPTIONS_GROUP,
-                              &keys_length,
-                              &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return NULL;
-  }
+  retro_try_propagate_val ({
+    keys = g_key_file_get_keys (self->key_file,
+                                RETRO_REFTEST_FILE_OPTIONS_GROUP,
+                                &keys_length,
+                                &catch);
+  }, catch, error, NULL);
 
   options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_strfreev);
   for (gsize i = 0; i < keys_length; i++) {
     g_auto (GStrv) values = NULL;
 
-    values = g_key_file_get_string_list (self->key_file,
-                                         RETRO_REFTEST_FILE_OPTIONS_GROUP,
-                                         keys[i],
-                                         NULL,
-                                         &tmp_error);
-    if (G_UNLIKELY (tmp_error != NULL)) {
-      g_propagate_error (error, tmp_error);
-
-      return NULL;
-    }
+    retro_try_propagate_val ({
+      values = g_key_file_get_string_list (self->key_file,
+                                           RETRO_REFTEST_FILE_OPTIONS_GROUP,
+                                           keys[i],
+                                           NULL,
+                                           &catch);
+    }, catch, error, NULL);
 
     g_hash_table_insert (options, g_strdup (keys[i]), g_steal_pointer (&values));
   }
@@ -453,31 +444,24 @@ retro_reftest_file_get_controllers (RetroReftestFile  *self,
   g_auto (GStrv) controller_names = NULL;
   g_autoptr (GArray) controllers = NULL;
   RetroControllerType type;
-  GError *tmp_error = NULL;
 
-  has_controllers = g_key_file_has_key (self->key_file,
-                                        RETRO_REFTEST_FILE_RETRO_REFTEST_GROUP,
-                                        RETRO_REFTEST_FILE_CONTROLLERS_KEY,
-                                        &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return NULL;
-  }
+  retro_try_propagate_val ({
+    has_controllers = g_key_file_has_key (self->key_file,
+                                          RETRO_REFTEST_FILE_RETRO_REFTEST_GROUP,
+                                          RETRO_REFTEST_FILE_CONTROLLERS_KEY,
+                                          &catch);
+  }, catch, error, NULL);
 
   if (!has_controllers)
     return NULL;
 
-  controller_names = g_key_file_get_string_list (self->key_file,
-                                                 RETRO_REFTEST_FILE_RETRO_REFTEST_GROUP,
-                                                 RETRO_REFTEST_FILE_CONTROLLERS_KEY,
-                                                 length,
-                                                 &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return NULL;
-  }
+  retro_try_propagate_val ({
+    controller_names = g_key_file_get_string_list (self->key_file,
+                                                   RETRO_REFTEST_FILE_RETRO_REFTEST_GROUP,
+                                                   RETRO_REFTEST_FILE_CONTROLLERS_KEY,
+                                                   length,
+                                                   &catch);
+  }, catch, error, NULL);
 
   controllers = g_array_sized_new (TRUE, TRUE, sizeof (RetroTestController *), *length);
   g_array_set_clear_func (controllers, (GDestroyNotify) g_object_pointer_unref);
@@ -584,17 +568,13 @@ retro_reftest_file_get_video (RetroReftestFile  *self,
                               GError           **error)
 {
   g_autofree gchar *key_file_video = NULL;
-  GError *tmp_error = NULL;
 
-  key_file_video = g_key_file_get_string (self->key_file,
-                                          g_hash_table_lookup (self->frames, &frame),
-                                          RETRO_REFTEST_FILE_FRAME_VIDEO_KEY,
-                                          &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return NULL;
-  }
+  retro_try_propagate_val ({
+    key_file_video = g_key_file_get_string (self->key_file,
+                                            g_hash_table_lookup (self->frames, &frame),
+                                            RETRO_REFTEST_FILE_FRAME_VIDEO_KEY,
+                                            &catch);
+  }, catch, error, NULL);
 
   return get_sibling (self, key_file_video);
 }
@@ -609,15 +589,11 @@ retro_reftest_file_get_controller_states (RetroReftestFile  *self,
   g_auto (GStrv) keys = NULL;
   RetroControllerState *state;
   GArray *states;
-  GError *tmp_error = NULL;
 
   group = g_hash_table_lookup (self->frames, &frame);
-  keys = g_key_file_get_keys (self->key_file, group, NULL, &tmp_error);
-  if (G_UNLIKELY (tmp_error != NULL)) {
-    g_propagate_error (error, tmp_error);
-
-    return NULL;
-  }
+  retro_try_propagate_val ({
+    keys = g_key_file_get_keys (self->key_file, group, NULL, &catch);
+  }, catch, error, NULL);
 
   controllers = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, (GDestroyNotify) g_array_unref);
 
@@ -631,21 +607,21 @@ retro_reftest_file_get_controller_states (RetroReftestFile  *self,
 
     controller_number_string = *key_i + strlen (RETRO_REFTEST_FILE_FRAME_CONTROLLER_PREFIX);
     controller_number = g_new (guint, 1);
-    *controller_number = str_to_uint (controller_number_string, &tmp_error);
-    if (G_UNLIKELY (tmp_error != NULL)) {
-      g_critical ("Invalid controller key [%s]: %s", *key_i, tmp_error->message);
-      g_clear_error (&tmp_error);
+    retro_try ({
+      *controller_number = str_to_uint (controller_number_string, &catch);
+    }, catch, {
+      g_critical ("Invalid controller key [%s]: %s", *key_i, catch->message);
 
       continue;
-    }
+    });
 
-    inputs = g_key_file_get_string_list (self->key_file, group, *key_i, NULL, &tmp_error);
-    if (G_UNLIKELY (tmp_error != NULL)) {
-      g_critical ("%s", tmp_error->message);
-      g_clear_error (&tmp_error);
+    retro_try ({
+      inputs = g_key_file_get_string_list (self->key_file, group, *key_i, NULL, &catch);
+    }, catch, {
+      g_critical ("%s", catch->message);
 
       continue;
-    }
+    });
 
     states = g_array_new (TRUE, TRUE, sizeof (RetroControllerState *));
     g_array_set_clear_func (states, (GDestroyNotify) g_pointer_free);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]