summaryrefslogtreecommitdiff
path: root/gdbsupport/common-exceptions.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdbsupport/common-exceptions.c')
-rw-r--r--gdbsupport/common-exceptions.c235
1 files changed, 235 insertions, 0 deletions
diff --git a/gdbsupport/common-exceptions.c b/gdbsupport/common-exceptions.c
new file mode 100644
index 00000000000..0cc515ba792
--- /dev/null
+++ b/gdbsupport/common-exceptions.c
@@ -0,0 +1,235 @@
+/* Exception (throw catch) mechanism, for GDB, the GNU debugger.
+
+ Copyright (C) 1986-2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "common-defs.h"
+#include "common-exceptions.h"
+#include <forward_list>
+
+/* Possible catcher states. */
+enum catcher_state {
+ /* Initial state, a new catcher has just been created. */
+ CATCHER_CREATED,
+ /* The catch code is running. */
+ CATCHER_RUNNING,
+ CATCHER_RUNNING_1,
+ /* The catch code threw an exception. */
+ CATCHER_ABORTING
+};
+
+/* Possible catcher actions. */
+enum catcher_action {
+ CATCH_ITER,
+ CATCH_ITER_1,
+ CATCH_THROWING
+};
+
+struct catcher
+{
+ enum catcher_state state = CATCHER_CREATED;
+ /* Jump buffer pointing back at the exception handler. */
+ jmp_buf buf;
+ /* Status buffer belonging to the exception handler. */
+ struct gdb_exception exception;
+};
+
+/* Where to go for throw_exception(). */
+static std::forward_list<struct catcher> catchers;
+
+jmp_buf *
+exceptions_state_mc_init ()
+{
+ catchers.emplace_front ();
+ return &catchers.front ().buf;
+}
+
+/* Catcher state machine. Returns non-zero if the m/c should be run
+ again, zero if it should abort. */
+
+static int
+exceptions_state_mc (enum catcher_action action)
+{
+ switch (catchers.front ().state)
+ {
+ case CATCHER_CREATED:
+ switch (action)
+ {
+ case CATCH_ITER:
+ /* Allow the code to run the catcher. */
+ catchers.front ().state = CATCHER_RUNNING;
+ return 1;
+ default:
+ internal_error (__FILE__, __LINE__, _("bad state"));
+ }
+ case CATCHER_RUNNING:
+ switch (action)
+ {
+ case CATCH_ITER:
+ /* No error/quit has occured. */
+ return 0;
+ case CATCH_ITER_1:
+ catchers.front ().state = CATCHER_RUNNING_1;
+ return 1;
+ case CATCH_THROWING:
+ catchers.front ().state = CATCHER_ABORTING;
+ /* See also throw_exception. */
+ return 1;
+ default:
+ internal_error (__FILE__, __LINE__, _("bad switch"));
+ }
+ case CATCHER_RUNNING_1:
+ switch (action)
+ {
+ case CATCH_ITER:
+ /* The did a "break" from the inner while loop. */
+ return 0;
+ case CATCH_ITER_1:
+ catchers.front ().state = CATCHER_RUNNING;
+ return 0;
+ case CATCH_THROWING:
+ catchers.front ().state = CATCHER_ABORTING;
+ /* See also throw_exception. */
+ return 1;
+ default:
+ internal_error (__FILE__, __LINE__, _("bad switch"));
+ }
+ case CATCHER_ABORTING:
+ switch (action)
+ {
+ case CATCH_ITER:
+ {
+ /* Exit normally if this catcher can handle this
+ exception. The caller analyses the func return
+ values. */
+ return 0;
+ }
+ default:
+ internal_error (__FILE__, __LINE__, _("bad state"));
+ }
+ default:
+ internal_error (__FILE__, __LINE__, _("bad switch"));
+ }
+}
+
+int
+exceptions_state_mc_catch (struct gdb_exception *exception,
+ int mask)
+{
+ *exception = std::move (catchers.front ().exception);
+ catchers.pop_front ();
+
+ if (exception->reason < 0)
+ {
+ if (mask & RETURN_MASK (exception->reason))
+ {
+ /* Exit normally and let the caller handle the
+ exception. */
+ return 1;
+ }
+
+ /* The caller didn't request that the event be caught, relay the
+ event to the next exception_catch/CATCH_SJLJ. */
+ throw_exception_sjlj (*exception);
+ }
+
+ /* No exception was thrown. */
+ return 0;
+}
+
+int
+exceptions_state_mc_action_iter (void)
+{
+ return exceptions_state_mc (CATCH_ITER);
+}
+
+int
+exceptions_state_mc_action_iter_1 (void)
+{
+ return exceptions_state_mc (CATCH_ITER_1);
+}
+
+/* Return EXCEPTION to the nearest containing CATCH_SJLJ block. */
+
+void
+throw_exception_sjlj (const struct gdb_exception &exception)
+{
+ /* Jump to the nearest CATCH_SJLJ block, communicating REASON to
+ that call via setjmp's return value. Note that REASON can't be
+ zero, by definition in common-exceptions.h. */
+ exceptions_state_mc (CATCH_THROWING);
+ enum return_reason reason = exception.reason;
+ catchers.front ().exception = exception;
+ longjmp (catchers.front ().buf, reason);
+}
+
+/* Implementation of throw_exception that uses C++ try/catch. */
+
+void
+throw_exception (gdb_exception &&exception)
+{
+ if (exception.reason == RETURN_QUIT)
+ throw gdb_exception_quit (std::move (exception));
+ else if (exception.reason == RETURN_ERROR)
+ throw gdb_exception_error (std::move (exception));
+ else
+ gdb_assert_not_reached ("invalid return reason");
+}
+
+static void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (3, 0)
+throw_it (enum return_reason reason, enum errors error, const char *fmt,
+ va_list ap)
+{
+ if (reason == RETURN_QUIT)
+ throw gdb_exception_quit (fmt, ap);
+ else if (reason == RETURN_ERROR)
+ throw gdb_exception_error (error, fmt, ap);
+ else
+ gdb_assert_not_reached ("invalid return reason");
+}
+
+void
+throw_verror (enum errors error, const char *fmt, va_list ap)
+{
+ throw_it (RETURN_ERROR, error, fmt, ap);
+}
+
+void
+throw_vquit (const char *fmt, va_list ap)
+{
+ throw_it (RETURN_QUIT, GDB_NO_ERROR, fmt, ap);
+}
+
+void
+throw_error (enum errors error, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ throw_verror (error, fmt, args);
+ va_end (args);
+}
+
+void
+throw_quit (const char *fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ throw_vquit (fmt, args);
+ va_end (args);
+}