xref: /netbsd-src/external/gpl3/gdb/dist/gdbsupport/common-exceptions.cc (revision 5ba1f45f2a09259cc846f20c7c5501604d633c90)
18dffb485Schristos /* Exception (throw catch) mechanism, for GDB, the GNU debugger.
28dffb485Schristos 
3*5ba1f45fSchristos    Copyright (C) 1986-2024 Free Software Foundation, Inc.
48dffb485Schristos 
58dffb485Schristos    This file is part of GDB.
68dffb485Schristos 
78dffb485Schristos    This program is free software; you can redistribute it and/or modify
88dffb485Schristos    it under the terms of the GNU General Public License as published by
98dffb485Schristos    the Free Software Foundation; either version 3 of the License, or
108dffb485Schristos    (at your option) any later version.
118dffb485Schristos 
128dffb485Schristos    This program is distributed in the hope that it will be useful,
138dffb485Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
148dffb485Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
158dffb485Schristos    GNU General Public License for more details.
168dffb485Schristos 
178dffb485Schristos    You should have received a copy of the GNU General Public License
188dffb485Schristos    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
198dffb485Schristos 
208dffb485Schristos #include "common-exceptions.h"
218dffb485Schristos #include <forward_list>
228dffb485Schristos 
238dffb485Schristos /* Possible catcher states.  */
248dffb485Schristos enum catcher_state {
258dffb485Schristos   /* Initial state, a new catcher has just been created.  */
268dffb485Schristos   CATCHER_CREATED,
278dffb485Schristos   /* The catch code is running.  */
288dffb485Schristos   CATCHER_RUNNING,
298dffb485Schristos   CATCHER_RUNNING_1,
308dffb485Schristos   /* The catch code threw an exception.  */
318dffb485Schristos   CATCHER_ABORTING
328dffb485Schristos };
338dffb485Schristos 
348dffb485Schristos /* Possible catcher actions.  */
358dffb485Schristos enum catcher_action {
368dffb485Schristos   CATCH_ITER,
378dffb485Schristos   CATCH_ITER_1,
388dffb485Schristos   CATCH_THROWING
398dffb485Schristos };
408dffb485Schristos 
418dffb485Schristos struct catcher
428dffb485Schristos {
438dffb485Schristos   enum catcher_state state = CATCHER_CREATED;
448dffb485Schristos   /* Jump buffer pointing back at the exception handler.  */
458dffb485Schristos   jmp_buf buf;
468dffb485Schristos   /* Status buffer belonging to the exception handler.  */
478dffb485Schristos   struct gdb_exception exception;
488dffb485Schristos };
498dffb485Schristos 
508dffb485Schristos /* Where to go for throw_exception().  */
518dffb485Schristos static std::forward_list<struct catcher> catchers;
528dffb485Schristos 
538dffb485Schristos jmp_buf *
548dffb485Schristos exceptions_state_mc_init ()
558dffb485Schristos {
568dffb485Schristos   catchers.emplace_front ();
578dffb485Schristos   return &catchers.front ().buf;
588dffb485Schristos }
598dffb485Schristos 
608dffb485Schristos /* Catcher state machine.  Returns non-zero if the m/c should be run
618dffb485Schristos    again, zero if it should abort.  */
628dffb485Schristos 
638dffb485Schristos static int
648dffb485Schristos exceptions_state_mc (enum catcher_action action)
658dffb485Schristos {
668dffb485Schristos   switch (catchers.front ().state)
678dffb485Schristos     {
688dffb485Schristos     case CATCHER_CREATED:
698dffb485Schristos       switch (action)
708dffb485Schristos 	{
718dffb485Schristos 	case CATCH_ITER:
728dffb485Schristos 	  /* Allow the code to run the catcher.  */
738dffb485Schristos 	  catchers.front ().state = CATCHER_RUNNING;
748dffb485Schristos 	  return 1;
758dffb485Schristos 	default:
764b169a6bSchristos 	  internal_error (_("bad state"));
778dffb485Schristos 	}
788dffb485Schristos     case CATCHER_RUNNING:
798dffb485Schristos       switch (action)
808dffb485Schristos 	{
818dffb485Schristos 	case CATCH_ITER:
82*5ba1f45fSchristos 	  /* No error/quit has occurred.  */
838dffb485Schristos 	  return 0;
848dffb485Schristos 	case CATCH_ITER_1:
858dffb485Schristos 	  catchers.front ().state = CATCHER_RUNNING_1;
868dffb485Schristos 	  return 1;
878dffb485Schristos 	case CATCH_THROWING:
888dffb485Schristos 	  catchers.front ().state = CATCHER_ABORTING;
898dffb485Schristos 	  /* See also throw_exception.  */
908dffb485Schristos 	  return 1;
918dffb485Schristos 	default:
924b169a6bSchristos 	  internal_error (_("bad switch"));
938dffb485Schristos 	}
948dffb485Schristos     case CATCHER_RUNNING_1:
958dffb485Schristos       switch (action)
968dffb485Schristos 	{
978dffb485Schristos 	case CATCH_ITER:
988dffb485Schristos 	  /* The did a "break" from the inner while loop.  */
998dffb485Schristos 	  return 0;
1008dffb485Schristos 	case CATCH_ITER_1:
1018dffb485Schristos 	  catchers.front ().state = CATCHER_RUNNING;
1028dffb485Schristos 	  return 0;
1038dffb485Schristos 	case CATCH_THROWING:
1048dffb485Schristos 	  catchers.front ().state = CATCHER_ABORTING;
1058dffb485Schristos 	  /* See also throw_exception.  */
1068dffb485Schristos 	  return 1;
1078dffb485Schristos 	default:
1084b169a6bSchristos 	  internal_error (_("bad switch"));
1098dffb485Schristos 	}
1108dffb485Schristos     case CATCHER_ABORTING:
1118dffb485Schristos       switch (action)
1128dffb485Schristos 	{
1138dffb485Schristos 	case CATCH_ITER:
1148dffb485Schristos 	  {
1158dffb485Schristos 	    /* Exit normally if this catcher can handle this
1168dffb485Schristos 	       exception.  The caller analyses the func return
1178dffb485Schristos 	       values.  */
1188dffb485Schristos 	    return 0;
1198dffb485Schristos 	  }
1208dffb485Schristos 	default:
1214b169a6bSchristos 	  internal_error (_("bad state"));
1228dffb485Schristos 	}
1238dffb485Schristos     default:
1244b169a6bSchristos       internal_error (_("bad switch"));
1258dffb485Schristos     }
1268dffb485Schristos }
1278dffb485Schristos 
1288dffb485Schristos int
1298dffb485Schristos exceptions_state_mc_catch (struct gdb_exception *exception,
1308dffb485Schristos 			   int mask)
1318dffb485Schristos {
1328dffb485Schristos   *exception = std::move (catchers.front ().exception);
1338dffb485Schristos   catchers.pop_front ();
1348dffb485Schristos 
1358dffb485Schristos   if (exception->reason < 0)
1368dffb485Schristos     {
1378dffb485Schristos       if (mask & RETURN_MASK (exception->reason))
1388dffb485Schristos 	{
1398dffb485Schristos 	  /* Exit normally and let the caller handle the
1408dffb485Schristos 	     exception.  */
1418dffb485Schristos 	  return 1;
1428dffb485Schristos 	}
1438dffb485Schristos 
1448dffb485Schristos       /* The caller didn't request that the event be caught, relay the
1458dffb485Schristos 	 event to the next exception_catch/CATCH_SJLJ.  */
1468dffb485Schristos       throw_exception_sjlj (*exception);
1478dffb485Schristos     }
1488dffb485Schristos 
1498dffb485Schristos   /* No exception was thrown.  */
1508dffb485Schristos   return 0;
1518dffb485Schristos }
1528dffb485Schristos 
1538dffb485Schristos int
1548dffb485Schristos exceptions_state_mc_action_iter (void)
1558dffb485Schristos {
1568dffb485Schristos   return exceptions_state_mc (CATCH_ITER);
1578dffb485Schristos }
1588dffb485Schristos 
1598dffb485Schristos int
1608dffb485Schristos exceptions_state_mc_action_iter_1 (void)
1618dffb485Schristos {
1628dffb485Schristos   return exceptions_state_mc (CATCH_ITER_1);
1638dffb485Schristos }
1648dffb485Schristos 
1658dffb485Schristos /* Return EXCEPTION to the nearest containing CATCH_SJLJ block.  */
1668dffb485Schristos 
1678dffb485Schristos void
1688dffb485Schristos throw_exception_sjlj (const struct gdb_exception &exception)
1698dffb485Schristos {
1708dffb485Schristos   /* Jump to the nearest CATCH_SJLJ block, communicating REASON to
1718dffb485Schristos      that call via setjmp's return value.  Note that REASON can't be
1728dffb485Schristos      zero, by definition in common-exceptions.h.  */
1738dffb485Schristos   exceptions_state_mc (CATCH_THROWING);
1748dffb485Schristos   enum return_reason reason = exception.reason;
1758dffb485Schristos   catchers.front ().exception = exception;
1768dffb485Schristos   longjmp (catchers.front ().buf, reason);
1778dffb485Schristos }
1788dffb485Schristos 
1798dffb485Schristos /* Implementation of throw_exception that uses C++ try/catch.  */
1808dffb485Schristos 
1818dffb485Schristos void
1828dffb485Schristos throw_exception (gdb_exception &&exception)
1838dffb485Schristos {
1848dffb485Schristos   if (exception.reason == RETURN_QUIT)
1858dffb485Schristos     throw gdb_exception_quit (std::move (exception));
186*5ba1f45fSchristos   else if (exception.reason == RETURN_FORCED_QUIT)
187*5ba1f45fSchristos     throw gdb_exception_forced_quit (std::move (exception));
1888dffb485Schristos   else if (exception.reason == RETURN_ERROR)
1898dffb485Schristos     throw gdb_exception_error (std::move (exception));
1908dffb485Schristos   else
1918dffb485Schristos     gdb_assert_not_reached ("invalid return reason");
1928dffb485Schristos }
1938dffb485Schristos 
1948dffb485Schristos static void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (3, 0)
1958dffb485Schristos throw_it (enum return_reason reason, enum errors error, const char *fmt,
1968dffb485Schristos 	  va_list ap)
1978dffb485Schristos {
1988dffb485Schristos   if (reason == RETURN_QUIT)
1998dffb485Schristos     throw gdb_exception_quit (fmt, ap);
200*5ba1f45fSchristos   else if (reason == RETURN_FORCED_QUIT)
201*5ba1f45fSchristos     throw gdb_exception_forced_quit (fmt, ap);
2028dffb485Schristos   else if (reason == RETURN_ERROR)
2038dffb485Schristos     throw gdb_exception_error (error, fmt, ap);
2048dffb485Schristos   else
2058dffb485Schristos     gdb_assert_not_reached ("invalid return reason");
2068dffb485Schristos }
2078dffb485Schristos 
2088dffb485Schristos void
2098dffb485Schristos throw_verror (enum errors error, const char *fmt, va_list ap)
2108dffb485Schristos {
2118dffb485Schristos   throw_it (RETURN_ERROR, error, fmt, ap);
2128dffb485Schristos }
2138dffb485Schristos 
2148dffb485Schristos void
2158dffb485Schristos throw_vquit (const char *fmt, va_list ap)
2168dffb485Schristos {
2178dffb485Schristos   throw_it (RETURN_QUIT, GDB_NO_ERROR, fmt, ap);
2188dffb485Schristos }
2198dffb485Schristos 
2208dffb485Schristos void
2218dffb485Schristos throw_error (enum errors error, const char *fmt, ...)
2228dffb485Schristos {
2238dffb485Schristos   va_list args;
2248dffb485Schristos 
2258dffb485Schristos   va_start (args, fmt);
2268dffb485Schristos   throw_verror (error, fmt, args);
2278dffb485Schristos   va_end (args);
2288dffb485Schristos }
2298dffb485Schristos 
2308dffb485Schristos void
2318dffb485Schristos throw_quit (const char *fmt, ...)
2328dffb485Schristos {
2338dffb485Schristos   va_list args;
2348dffb485Schristos 
2358dffb485Schristos   va_start (args, fmt);
2368dffb485Schristos   throw_vquit (fmt, args);
2378dffb485Schristos   va_end (args);
2388dffb485Schristos }
239*5ba1f45fSchristos 
240*5ba1f45fSchristos void
241*5ba1f45fSchristos throw_forced_quit (const char *fmt, ...)
242*5ba1f45fSchristos {
243*5ba1f45fSchristos   va_list args;
244*5ba1f45fSchristos 
245*5ba1f45fSchristos   va_start (args, fmt);
246*5ba1f45fSchristos   throw_it (RETURN_FORCED_QUIT, GDB_NO_ERROR, fmt, args);
247*5ba1f45fSchristos   va_end (args);
248*5ba1f45fSchristos }
249