Some gdb_exception{,error,quit} tweaks
[binutils-gdb.git] / gdb / common / common-exceptions.h
index d2c0beef9def1fa98d13d92f4b277699085f98cb..3f47caec7757ef88b5b5642db40edffc5f0f0a38 100644 (file)
@@ -1,6 +1,6 @@
 /* Exception (throw catch) mechanism, for GDB, the GNU debugger.
 
-   Copyright (C) 1986-2015 Free Software Foundation, Inc.
+   Copyright (C) 1986-2019 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
-#ifndef COMMON_EXCEPTIONS_H
-#define COMMON_EXCEPTIONS_H
+#ifndef COMMON_COMMON_EXCEPTIONS_H
+#define COMMON_COMMON_EXCEPTIONS_H
 
-#include "gdb_setjmp.h"
+#include <setjmp.h>
+#include <new>
+#include <memory>
+#include <string>
 
 /* Reasons for calling throw_exceptions().  NOTE: all reason values
-   must be less than zero.  enum value 0 is reserved for internal use
-   as the return value from an initial setjmp().  The function
-   catch_exceptions() reserves values >= 0 as legal results from its
-   wrapped function.  */
+   must be different from zero.  enum value 0 is reserved for internal
+   use as the return value from an initial setjmp().  */
 
 enum return_reason
   {
@@ -86,7 +87,7 @@ enum errors {
      means the register was not saved in the frame.  */
   OPTIMIZED_OUT_ERROR,
 
-  /* DW_OP_GNU_entry_value resolving failed.  */
+  /* DW_OP_entry_value resolving failed.  */
   NO_ENTRY_VALUE_ERROR,
 
   /* Target throwing an error has been closed.  Current command should be
@@ -111,17 +112,64 @@ 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 exceptions state machine.  Though declared
-   here by necessity, these functions should be considered internal to
-   the exceptions subsystem and not used other than via the TRY/CATCH
-   macros defined below.  */
+/* Functions to drive the sjlj-based exceptions state machine.  Though
+   declared here by necessity, these functions should be considered
+   internal to the exceptions subsystem and not used other than via
+   the TRY/CATCH (or TRY_SJLJ/CATCH_SJLJ) macros defined below.  */
 
-extern SIGJMP_BUF *exceptions_state_mc_init (void);
+extern jmp_buf *exceptions_state_mc_init (void);
 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);
@@ -137,58 +185,107 @@ extern int exceptions_state_mc_catch (struct gdb_exception *, int);
 
    *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
 
-  */
+   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 \
+#define TRY_SJLJ \
      { \
-       SIGJMP_BUF *buf = \
+       jmp_buf *buf = \
         exceptions_state_mc_init (); \
-       SIGSETJMP (*buf); \
+       setjmp (*buf); \
      } \
      while (exceptions_state_mc_action_iter ()) \
        while (exceptions_state_mc_action_iter_1 ())
 
-#define CATCH(EXCEPTION, MASK)                         \
+#define CATCH_SJLJ(EXCEPTION, MASK)                            \
   {                                                    \
     struct gdb_exception EXCEPTION;                            \
     if (exceptions_state_mc_catch (&(EXCEPTION), MASK))
 
-#define END_CATCH                              \
+#define END_CATCH_SJLJ                         \
   }
 
-/* *INDENT-ON* */
+/* 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_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)
+  {
+  }
 
-/* Hook to allow client-specific actions to be performed prior to
-   throwing an exception.  This function must be provided by the
-   client, and will be called before any cleanups are run.  */
+  explicit gdb_exception_error (const gdb_exception &ex) noexcept
+    : gdb_exception (ex)
+  {
+    gdb_assert (ex.reason == RETURN_ERROR);
+  }
+};
 
-extern void prepare_to_throw_exception (void);
+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)
+  {
+  }
 
-/* Throw an exception (as described by "struct gdb_exception").  Will
-   execute a LONG JUMP to the inner most containing exception handler
-   established using catch_exceptions() (or similar).
+  explicit gdb_exception_quit (const gdb_exception &ex) noexcept
+    : gdb_exception (ex)
+  {
+    gdb_assert (ex.reason == RETURN_QUIT);
+  }
+};
 
-   Code normally throws an exception using error() et.al.  For various
-   reaons, GDB also contains code that throws an exception directly.
-   For instance, the remote*.c targets contain CNTRL-C signal handlers
-   that propogate the QUIT event up the exception chain.  ``This could
-   be a good thing or a dangerous thing.'' -- the Existential
-   Wombat.  */
+/* An exception type that inherits from both std::bad_alloc and a gdb
+   exception.  This is necessary because operator new can only throw
+   std::bad_alloc, and OTOH, we want exceptions thrown due to memory
+   allocation error to be caught by all the CATCH/RETURN_MASK_ALL
+   spread around the codebase.  */
 
-extern void throw_exception (struct gdb_exception exception)
+struct gdb_quit_bad_alloc
+  : public gdb_exception_quit,
+    public std::bad_alloc
+{
+  explicit gdb_quit_bad_alloc (const gdb_exception &ex) noexcept
+    : gdb_exception_quit (ex),
+      std::bad_alloc ()
+  {
+  }
+};
+
+/* *INDENT-ON* */
+
+/* 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 (const gdb_exception &exception)
      ATTRIBUTE_NORETURN;
+
+/* Throw an exception by executing a LONG JUMP to the inner most
+   containing exception handler established using TRY_SJLJ.  Necessary
+   in some cases where we need to throw GDB exceptions across
+   third-party library code (e.g., readline).  */
+extern void throw_exception_sjlj (struct gdb_exception exception)
+     ATTRIBUTE_NORETURN;
+
+/* Convenience wrappers around throw_exception that throw GDB
+   errors.  */
 extern void throw_verror (enum errors, const char *fmt, va_list ap)
      ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (2, 0);
 extern void throw_vquit (const char *fmt, va_list ap)
@@ -201,4 +298,4 @@ extern void throw_quit (const char *fmt, ...)
 /* A pre-defined non-exception.  */
 extern const struct gdb_exception exception_none;
 
-#endif /* COMMON_EXCEPTIONS_H */
+#endif /* COMMON_COMMON_EXCEPTIONS_H */