xref: /netbsd-src/external/gpl3/gdb/dist/gdbsupport/common-exceptions.cc (revision 9fb66d812c00ebfb445c0b47dea128f32aa6fe96)
1 /* Exception (throw catch) mechanism, for GDB, the GNU debugger.
2 
3    Copyright (C) 1986-2020 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.  */
25 enum 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.  */
36 enum catcher_action {
37   CATCH_ITER,
38   CATCH_ITER_1,
39   CATCH_THROWING
40 };
41 
42 struct 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().  */
52 static std::forward_list<struct catcher> catchers;
53 
54 jmp_buf *
55 exceptions_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 
64 static int
65 exceptions_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 (__FILE__, __LINE__, _("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 (__FILE__, __LINE__, _("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 (__FILE__, __LINE__, _("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 (__FILE__, __LINE__, _("bad state"));
123 	}
124     default:
125       internal_error (__FILE__, __LINE__, _("bad switch"));
126     }
127 }
128 
129 int
130 exceptions_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 
154 int
155 exceptions_state_mc_action_iter (void)
156 {
157   return exceptions_state_mc (CATCH_ITER);
158 }
159 
160 int
161 exceptions_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 
168 void
169 throw_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 
182 void
183 throw_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 
193 static void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (3, 0)
194 throw_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 
205 void
206 throw_verror (enum errors error, const char *fmt, va_list ap)
207 {
208   throw_it (RETURN_ERROR, error, fmt, ap);
209 }
210 
211 void
212 throw_vquit (const char *fmt, va_list ap)
213 {
214   throw_it (RETURN_QUIT, GDB_NO_ERROR, fmt, ap);
215 }
216 
217 void
218 throw_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 
227 void
228 throw_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