Some gdb_exception{,error,quit} tweaks
[binutils-gdb.git] / gdb / common / common-exceptions.h
index 6cc09eab93851c60b2f8ad53b56b44a9db0d9744..3f47caec7757ef88b5b5642db40edffc5f0f0a38 100644 (file)
@@ -22,6 +22,8 @@
 
 #include <setjmp.h>
 #include <new>
+#include <memory>
+#include <string>
 
 /* Reasons for calling throw_exceptions().  NOTE: all reason values
    must be different from zero.  enum value 0 is reserved for internal
@@ -110,9 +112,56 @@ enum errors {
 
 struct gdb_exception
 {
+  gdb_exception ()
+    : reason ((enum return_reason) 0),
+      error (GDB_NO_ERROR)
+  {
+  }
+
+  gdb_exception (enum return_reason r, enum errors e)
+    : reason (r),
+      error (e)
+  {
+  }
+
+  gdb_exception (enum return_reason r, enum errors e,
+                const char *fmt, va_list ap)
+    ATTRIBUTE_PRINTF (4, 0)
+    : reason (r),
+      error (e),
+      message (std::make_shared<std::string> (string_vprintf (fmt, ap)))
+  {
+  }
+
+  /* The copy constructor exists so that we can mark it "noexcept",
+     which is a good practice for any sort of exception object.  */
+  gdb_exception (const gdb_exception &other) noexcept
+    : reason (other.reason),
+      error (other.error),
+      message (other.message)
+  {
+  }
+
+  /* The assignment operator exists so that we can mark it "noexcept",
+     which is a good practice for any sort of exception object.  */
+  gdb_exception &operator= (const gdb_exception &other) noexcept
+  {
+    reason = other.reason;
+    error = other.error;
+    message = other.message;
+    return *this;
+  }
+
+  /* Return the contents of the exception message, as a C string.  The
+     string remains owned by the exception object.  */
+  const char *what () const noexcept
+  {
+    return message->c_str ();
+  }
+
   enum return_reason reason;
   enum errors error;
-  const char *message;
+  std::shared_ptr<std::string> message;
 };
 
 /* Functions to drive the sjlj-based exceptions state machine.  Though
@@ -125,12 +174,6 @@ extern int exceptions_state_mc_action_iter (void);
 extern int exceptions_state_mc_action_iter_1 (void);
 extern int exceptions_state_mc_catch (struct gdb_exception *, int);
 
-/* For the C++ try/catch-based TRY/CATCH mechanism.  */
-
-extern void *exception_try_scope_entry (void);
-extern void exception_try_scope_exit (void *saved_state);
-extern void exception_rethrow (void) ATTRIBUTE_NORETURN;
-
 /* Macro to wrap up standard try/catch behavior.
 
    The double loop lets us correctly handle code "break"ing out of the
@@ -142,24 +185,21 @@ extern void exception_rethrow (void) ATTRIBUTE_NORETURN;
 
    *INDENT-OFF*
 
-   TRY
+   TRY_SJLJ
      {
      }
-   CATCH (e, RETURN_MASK_ERROR)
+   CATCH_SJLJ (e, RETURN_MASK_ERROR)
      {
        switch (e.reason)
          {
            case RETURN_ERROR: ...
          }
      }
-   END_CATCH
+   END_CATCH_SJLJ
 
-  Note that the SJLJ version of the macros are actually named
-  TRY_SJLJ/CATCH_SJLJ in order to make it possible to call them even
-  when TRY/CATCH are mapped to C++ try/catch.  The SJLJ variants are
-  needed in some cases where gdb exceptions need to cross third-party
-  library code compiled without exceptions support (e.g.,
-  readline).  */
+   The SJLJ variants are needed in some cases where gdb exceptions
+   need to cross third-party library code compiled without exceptions
+   support (e.g., readline).  */
 
 #define TRY_SJLJ \
      { \
@@ -178,72 +218,38 @@ extern void exception_rethrow (void) ATTRIBUTE_NORETURN;
 #define END_CATCH_SJLJ                         \
   }
 
-/* Prevent error/quit during TRY from calling cleanups established
-   prior to here.  This pops out the scope in either case of normal
-   exit or exception exit.  */
-struct exception_try_scope
-{
-  exception_try_scope ()
-  {
-    saved_state = exception_try_scope_entry ();
-  }
-  ~exception_try_scope ()
-  {
-    exception_try_scope_exit (saved_state);
-  }
-
-  void *saved_state;
-};
-
-/* We still need to wrap TRY/CATCH in C++ so that cleanups and C++
-   exceptions can coexist.
-
-   The TRY blocked is wrapped in a do/while(0) so that break/continue
-   within the block works the same as in C.
-
-   END_CATCH makes sure that even if the CATCH block doesn't want to
-   catch the exception, we stop at every frame in the unwind chain to
-   run its cleanups, which may e.g., have pointers to stack variables
-   that are going to be destroyed.
-
-   There's an outer scope around the whole TRY/END_CATCH in order to
-   cause a compilation error if you forget to add the END_CATCH at the
-   end a TRY/CATCH construct.  */
-
-#define TRY                                                            \
-  {                                                                    \
-    try                                                                        \
-      {                                                                        \
-       exception_try_scope exception_try_scope_instance;               \
-       do                                                              \
-         {
-
-#define CATCH(EXCEPTION, MASK)                                         \
-         } while (0);                                                  \
-       }                                                               \
-    catch (struct gdb_exception ## _ ## MASK &EXCEPTION)
-
-#define END_CATCH                              \
-    catch (...)                                        \
-      {                                                \
-       exception_rethrow ();                   \
-      }                                                \
-  }
-
 /* The exception types client code may catch.  They're just shims
    around gdb_exception that add nothing but type info.  Which is used
    is selected depending on the MASK argument passed to CATCH.  */
 
-struct gdb_exception_RETURN_MASK_ALL : public gdb_exception
+struct gdb_exception_error : public gdb_exception
 {
-};
+  gdb_exception_error (enum errors e, const char *fmt, va_list ap)
+    ATTRIBUTE_PRINTF (3, 0)
+    : gdb_exception (RETURN_ERROR, e, fmt, ap)
+  {
+  }
 
-struct gdb_exception_RETURN_MASK_ERROR : public gdb_exception_RETURN_MASK_ALL
-{
+  explicit gdb_exception_error (const gdb_exception &ex) noexcept
+    : gdb_exception (ex)
+  {
+    gdb_assert (ex.reason == RETURN_ERROR);
+  }
 };
 
-struct gdb_exception_RETURN_MASK_QUIT : public gdb_exception_RETURN_MASK_ALL
+struct gdb_exception_quit : public gdb_exception
 {
+  gdb_exception_quit (const char *fmt, va_list ap)
+    ATTRIBUTE_PRINTF (2, 0)
+    : gdb_exception (RETURN_QUIT, GDB_NO_ERROR, fmt, ap)
+  {
+  }
+
+  explicit gdb_exception_quit (const gdb_exception &ex) noexcept
+    : gdb_exception (ex)
+  {
+    gdb_assert (ex.reason == RETURN_QUIT);
+  }
 };
 
 /* An exception type that inherits from both std::bad_alloc and a gdb
@@ -253,15 +259,13 @@ struct gdb_exception_RETURN_MASK_QUIT : public gdb_exception_RETURN_MASK_ALL
    spread around the codebase.  */
 
 struct gdb_quit_bad_alloc
-  : public gdb_exception_RETURN_MASK_QUIT,
+  : public gdb_exception_quit,
     public std::bad_alloc
 {
-  explicit gdb_quit_bad_alloc (gdb_exception ex)
-    : std::bad_alloc ()
+  explicit gdb_quit_bad_alloc (const gdb_exception &ex) noexcept
+    : gdb_exception_quit (ex),
+      std::bad_alloc ()
   {
-    gdb_exception *self = this;
-
-    *self = ex;
   }
 };
 
@@ -270,7 +274,7 @@ struct gdb_quit_bad_alloc
 /* Throw an exception (as described by "struct gdb_exception"),
    landing in the inner most containing exception handler established
    using TRY/CATCH.  */
-extern void throw_exception (struct gdb_exception exception)
+extern void throw_exception (const gdb_exception &exception)
      ATTRIBUTE_NORETURN;
 
 /* Throw an exception by executing a LONG JUMP to the inner most