1/* Exception (throw catch) mechanism, for GDB, the GNU debugger.
2
3   Copyright (C) 1986-2023 Free Software Foundation, Inc.
4
5   This file is part of GDB.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20#include "common-defs.h"
21#include "common-exceptions.h"
22#include <forward_list>
23
24/* Possible catcher states.  */
25enum catcher_state {
26  /* Initial state, a new catcher has just been created.  */
27  CATCHER_CREATED,
28  /* The catch code is running.  */
29  CATCHER_RUNNING,
30  CATCHER_RUNNING_1,
31  /* The catch code threw an exception.  */
32  CATCHER_ABORTING
33};
34
35/* Possible catcher actions.  */
36enum catcher_action {
37  CATCH_ITER,
38  CATCH_ITER_1,
39  CATCH_THROWING
40};
41
42struct catcher
43{
44  enum catcher_state state = CATCHER_CREATED;
45  /* Jump buffer pointing back at the exception handler.  */
46  jmp_buf buf;
47  /* Status buffer belonging to the exception handler.  */
48  struct gdb_exception exception;
49};
50
51/* Where to go for throw_exception().  */
52static std::forward_list<struct catcher> catchers;
53
54jmp_buf *
55exceptions_state_mc_init ()
56{
57  catchers.emplace_front ();
58  return &catchers.front ().buf;
59}
60
61/* Catcher state machine.  Returns non-zero if the m/c should be run
62   again, zero if it should abort.  */
63
64static int
65exceptions_state_mc (enum catcher_action action)
66{
67  switch (catchers.front ().state)
68    {
69    case CATCHER_CREATED:
70      switch (action)
71	{
72	case CATCH_ITER:
73	  /* Allow the code to run the catcher.  */
74	  catchers.front ().state = CATCHER_RUNNING;
75	  return 1;
76	default:
77	  internal_error (_("bad state"));
78	}
79    case CATCHER_RUNNING:
80      switch (action)
81	{
82	case CATCH_ITER:
83	  /* No error/quit has occured.  */
84	  return 0;
85	case CATCH_ITER_1:
86	  catchers.front ().state = CATCHER_RUNNING_1;
87	  return 1;
88	case CATCH_THROWING:
89	  catchers.front ().state = CATCHER_ABORTING;
90	  /* See also throw_exception.  */
91	  return 1;
92	default:
93	  internal_error (_("bad switch"));
94	}
95    case CATCHER_RUNNING_1:
96      switch (action)
97	{
98	case CATCH_ITER:
99	  /* The did a "break" from the inner while loop.  */
100	  return 0;
101	case CATCH_ITER_1:
102	  catchers.front ().state = CATCHER_RUNNING;
103	  return 0;
104	case CATCH_THROWING:
105	  catchers.front ().state = CATCHER_ABORTING;
106	  /* See also throw_exception.  */
107	  return 1;
108	default:
109	  internal_error (_("bad switch"));
110	}
111    case CATCHER_ABORTING:
112      switch (action)
113	{
114	case CATCH_ITER:
115	  {
116	    /* Exit normally if this catcher can handle this
117	       exception.  The caller analyses the func return
118	       values.  */
119	    return 0;
120	  }
121	default:
122	  internal_error (_("bad state"));
123	}
124    default:
125      internal_error (_("bad switch"));
126    }
127}
128
129int
130exceptions_state_mc_catch (struct gdb_exception *exception,
131			   int mask)
132{
133  *exception = std::move (catchers.front ().exception);
134  catchers.pop_front ();
135
136  if (exception->reason < 0)
137    {
138      if (mask & RETURN_MASK (exception->reason))
139	{
140	  /* Exit normally and let the caller handle the
141	     exception.  */
142	  return 1;
143	}
144
145      /* The caller didn't request that the event be caught, relay the
146	 event to the next exception_catch/CATCH_SJLJ.  */
147      throw_exception_sjlj (*exception);
148    }
149
150  /* No exception was thrown.  */
151  return 0;
152}
153
154int
155exceptions_state_mc_action_iter (void)
156{
157  return exceptions_state_mc (CATCH_ITER);
158}
159
160int
161exceptions_state_mc_action_iter_1 (void)
162{
163  return exceptions_state_mc (CATCH_ITER_1);
164}
165
166/* Return EXCEPTION to the nearest containing CATCH_SJLJ block.  */
167
168void
169throw_exception_sjlj (const struct gdb_exception &exception)
170{
171  /* Jump to the nearest CATCH_SJLJ block, communicating REASON to
172     that call via setjmp's return value.  Note that REASON can't be
173     zero, by definition in common-exceptions.h.  */
174  exceptions_state_mc (CATCH_THROWING);
175  enum return_reason reason = exception.reason;
176  catchers.front ().exception = exception;
177  longjmp (catchers.front ().buf, reason);
178}
179
180/* Implementation of throw_exception that uses C++ try/catch.  */
181
182void
183throw_exception (gdb_exception &&exception)
184{
185  if (exception.reason == RETURN_QUIT)
186    throw gdb_exception_quit (std::move (exception));
187  else if (exception.reason == RETURN_ERROR)
188    throw gdb_exception_error (std::move (exception));
189  else
190    gdb_assert_not_reached ("invalid return reason");
191}
192
193static void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (3, 0)
194throw_it (enum return_reason reason, enum errors error, const char *fmt,
195	  va_list ap)
196{
197  if (reason == RETURN_QUIT)
198    throw gdb_exception_quit (fmt, ap);
199  else if (reason == RETURN_ERROR)
200    throw gdb_exception_error (error, fmt, ap);
201  else
202    gdb_assert_not_reached ("invalid return reason");
203}
204
205void
206throw_verror (enum errors error, const char *fmt, va_list ap)
207{
208  throw_it (RETURN_ERROR, error, fmt, ap);
209}
210
211void
212throw_vquit (const char *fmt, va_list ap)
213{
214  throw_it (RETURN_QUIT, GDB_NO_ERROR, fmt, ap);
215}
216
217void
218throw_error (enum errors error, const char *fmt, ...)
219{
220  va_list args;
221
222  va_start (args, fmt);
223  throw_verror (error, fmt, args);
224  va_end (args);
225}
226
227void
228throw_quit (const char *fmt, ...)
229{
230  va_list args;
231
232  va_start (args, fmt);
233  throw_vquit (fmt, args);
234  va_end (args);
235}
236