OSDN Git Service

2004-09-16 Andrew Pinski <apinski@apple.com>
[pf3gnuchains/gcc-fork.git] / libobjc / exception.c
1 /* The implementation of exception handling primitives for Objective-C.
2    Copyright (C) 2004 Free Software Foundation, Inc.
3
4 This file is part of GCC.
5
6 GCC is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
9 later version.
10
11 GCC is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
14 License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING.  If not, write to
18 the Free Software Foundation, 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.  */
20
21 /* As a special exception, if you link this library with files compiled
22    with GCC to produce an executable, this does not cause the resulting
23    executable to be covered by the GNU General Public License.  This
24    exception does not however invalidate any other reasons why the
25    executable file might be covered by the GNU General Public License. */
26
27 #include <stdlib.h>
28 #include "objc/objc-api.h"
29 #include "unwind.h"
30 #include "unwind-pe.h"
31
32 \f
33 /* This is the exception class we report -- "GNUCOBJC".  */
34 #define __objc_exception_class                  \
35   ((((((((_Unwind_Exception_Class) 'G'          \
36          << 8 | (_Unwind_Exception_Class) 'N')  \
37         << 8 | (_Unwind_Exception_Class) 'U')   \
38        << 8 | (_Unwind_Exception_Class) 'C')    \
39       << 8 | (_Unwind_Exception_Class) 'O')     \
40      << 8 | (_Unwind_Exception_Class) 'B')      \
41     << 8 | (_Unwind_Exception_Class) 'J')       \
42    << 8 | (_Unwind_Exception_Class) 'C')
43
44 /* This is the object that is passed around by the Objective C runtime
45    to represent the exception in flight.  */
46
47 struct ObjcException
48 {
49   /* This bit is needed in order to interact with the unwind runtime.  */
50   struct _Unwind_Exception base;
51
52   /* The actual object we want to throw.  */
53   id value;
54
55   /* Cache some internal unwind data between phase 1 and phase 2.  */
56   _Unwind_Ptr landingPad;
57   int handlerSwitchValue;
58 };
59
60 \f
61
62 struct lsda_header_info
63 {
64   _Unwind_Ptr Start;
65   _Unwind_Ptr LPStart;
66   _Unwind_Ptr ttype_base;
67   const unsigned char *TType;
68   const unsigned char *action_table;
69   unsigned char ttype_encoding;
70   unsigned char call_site_encoding;
71 };
72
73 static const unsigned char *
74 parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p,
75                    struct lsda_header_info *info)
76 {
77   _Unwind_Word tmp;
78   unsigned char lpstart_encoding;
79
80   info->Start = (context ? _Unwind_GetRegionStart (context) : 0);
81
82   /* Find @LPStart, the base to which landing pad offsets are relative.  */
83   lpstart_encoding = *p++;
84   if (lpstart_encoding != DW_EH_PE_omit)
85     p = read_encoded_value (context, lpstart_encoding, p, &info->LPStart);
86   else
87     info->LPStart = info->Start;
88
89   /* Find @TType, the base of the handler and exception spec type data.  */
90   info->ttype_encoding = *p++;
91   if (info->ttype_encoding != DW_EH_PE_omit)
92     {
93       p = read_uleb128 (p, &tmp);
94       info->TType = p + tmp;
95     }
96   else
97     info->TType = 0;
98
99   /* The encoding and length of the call-site table; the action table
100      immediately follows.  */
101   info->call_site_encoding = *p++;
102   p = read_uleb128 (p, &tmp);
103   info->action_table = p + tmp;
104
105   return p;
106 }
107
108 static Class
109 get_ttype_entry (struct lsda_header_info *info, _Unwind_Word i)
110 {
111   _Unwind_Ptr ptr;
112
113   i *= size_of_encoded_value (info->ttype_encoding);
114   read_encoded_value_with_base (info->ttype_encoding, info->ttype_base,
115                                 info->TType - i, &ptr);
116
117   /* NULL ptr means catch-all.  */
118   if (ptr)
119     return objc_get_class ((const char *) ptr);
120   else
121     return 0;
122 }
123
124 /* Like unto the method of the same name on Object, but takes an id.  */
125 /* ??? Does this bork the meta-type system?  Can/should we look up an
126    isKindOf method on the id?  */
127
128 static int
129 isKindOf (id value, Class target)
130 {
131   Class c;
132
133   /* NULL target is catch-all.  */
134   if (target == 0)
135     return 1;
136
137   for (c = value->class_pointer; c; c = class_get_super_class (c))
138     if (c == target)
139       return 1;
140   return 0;
141 }
142
143 /* Using a different personality function name causes link failures
144    when trying to mix code using different exception handling models.  */
145 #ifdef SJLJ_EXCEPTIONS
146 #define PERSONALITY_FUNCTION    __gnu_objc_personality_sj0
147 #define __builtin_eh_return_data_regno(x) x
148 #else
149 #define PERSONALITY_FUNCTION    __gnu_objc_personality_v0
150 #endif
151
152 _Unwind_Reason_Code
153 PERSONALITY_FUNCTION (int version,
154                       _Unwind_Action actions,
155                       _Unwind_Exception_Class exception_class,
156                       struct _Unwind_Exception *ue_header,
157                       struct _Unwind_Context *context)
158 {
159   struct ObjcException *xh = (struct ObjcException *) ue_header;
160
161   struct lsda_header_info info;
162   const unsigned char *language_specific_data;
163   const unsigned char *action_record;
164   const unsigned char *p;
165   _Unwind_Ptr landing_pad, ip;
166   int handler_switch_value;
167   int saw_cleanup, saw_handler;
168
169   /* Interface version check.  */
170   if (version != 1)
171     return _URC_FATAL_PHASE1_ERROR;
172
173   /* Shortcut for phase 2 found handler for domestic exception.  */
174   if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME)
175       && exception_class == __objc_exception_class)
176     {
177       handler_switch_value = xh->handlerSwitchValue;
178       landing_pad = xh->landingPad;
179       goto install_context;
180     }
181
182   language_specific_data = (const unsigned char *)
183     _Unwind_GetLanguageSpecificData (context);
184
185   /* If no LSDA, then there are no handlers or cleanups.  */
186   if (! language_specific_data)
187     return _URC_CONTINUE_UNWIND;
188
189   /* Parse the LSDA header.  */
190   p = parse_lsda_header (context, language_specific_data, &info);
191   info.ttype_base = base_of_encoded_value (info.ttype_encoding, context);
192   ip = _Unwind_GetIP (context) - 1;
193   landing_pad = 0;
194   action_record = 0;
195   handler_switch_value = 0;
196
197 #ifdef SJLJ_EXCEPTIONS
198   /* The given "IP" is an index into the call-site table, with two
199      exceptions -- -1 means no-action, and 0 means terminate.  But
200      since we're using uleb128 values, we've not got random access
201      to the array.  */
202   if ((int) ip < 0)
203     return _URC_CONTINUE_UNWIND;
204   else
205     {
206       _Unwind_Word cs_lp, cs_action;
207       do
208         {
209           p = read_uleb128 (p, &cs_lp);
210           p = read_uleb128 (p, &cs_action);
211         }
212       while (--ip);
213
214       /* Can never have null landing pad for sjlj -- that would have
215          been indicated by a -1 call site index.  */
216       landing_pad = cs_lp + 1;
217       if (cs_action)
218         action_record = info.action_table + cs_action - 1;
219       goto found_something;
220     }
221 #else
222   /* Search the call-site table for the action associated with this IP.  */
223   while (p < info.action_table)
224     {
225       _Unwind_Ptr cs_start, cs_len, cs_lp;
226       _Unwind_Word cs_action;
227
228       /* Note that all call-site encodings are "absolute" displacements.  */
229       p = read_encoded_value (0, info.call_site_encoding, p, &cs_start);
230       p = read_encoded_value (0, info.call_site_encoding, p, &cs_len);
231       p = read_encoded_value (0, info.call_site_encoding, p, &cs_lp);
232       p = read_uleb128 (p, &cs_action);
233
234       /* The table is sorted, so if we've passed the ip, stop.  */
235       if (ip < info.Start + cs_start)
236         p = info.action_table;
237       else if (ip < info.Start + cs_start + cs_len)
238         {
239           if (cs_lp)
240             landing_pad = info.LPStart + cs_lp;
241           if (cs_action)
242             action_record = info.action_table + cs_action - 1;
243           goto found_something;
244         }
245     }
246 #endif /* SJLJ_EXCEPTIONS  */
247
248   /* If ip is not present in the table, C++ would call terminate.  */
249   /* ??? As with Java, it's perhaps better to tweek the LSDA to
250      that no-action is mapped to no-entry.  */
251   return _URC_CONTINUE_UNWIND;
252
253  found_something:
254   saw_cleanup = 0;
255   saw_handler = 0;
256
257   if (landing_pad == 0)
258     {
259       /* If ip is present, and has a null landing pad, there are
260          no cleanups or handlers to be run.  */
261     }
262   else if (action_record == 0)
263     {
264       /* If ip is present, has a non-null landing pad, and a null
265          action table offset, then there are only cleanups present.
266          Cleanups use a zero switch value, as set above.  */
267       saw_cleanup = 1;
268     }
269   else
270     {
271       /* Otherwise we have a catch handler.  */
272       _Unwind_Sword ar_filter, ar_disp;
273
274       while (1)
275         {
276           p = action_record;
277           p = read_sleb128 (p, &ar_filter);
278           read_sleb128 (p, &ar_disp);
279
280           if (ar_filter == 0)
281             {
282               /* Zero filter values are cleanups.  */
283               saw_cleanup = 1;
284             }
285
286           /* During forced unwinding, we only run cleanups.  With a
287              foreign exception class, we have no class info to match.  */
288           else if ((actions & _UA_FORCE_UNWIND)
289                    || exception_class != __objc_exception_class)
290             ;
291
292           else if (ar_filter > 0)
293             {
294               /* Positive filter values are handlers.  */
295
296               Class catch_type = get_ttype_entry (&info, ar_filter);
297
298               if (isKindOf (xh->value, catch_type))
299                 {
300                   handler_switch_value = ar_filter;
301                   saw_handler = 1;
302                   break;
303                 }
304             }
305           else
306             {
307               /* Negative filter values are exception specifications,
308                  which Objective-C does not use.  */
309               abort ();
310             }
311
312           if (ar_disp == 0)
313             break;
314           action_record = p + ar_disp;
315         }
316     }
317
318   if (! saw_handler && ! saw_cleanup)
319     return _URC_CONTINUE_UNWIND;
320
321   if (actions & _UA_SEARCH_PHASE)
322     {
323       if (!saw_handler)
324         return _URC_CONTINUE_UNWIND;
325
326       /* For domestic exceptions, we cache data from phase 1 for phase 2.  */
327       if (exception_class == __objc_exception_class)
328         {
329           xh->handlerSwitchValue = handler_switch_value;
330           xh->landingPad = landing_pad;
331         }
332       return _URC_HANDLER_FOUND;
333     }
334
335  install_context:
336   _Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
337                  __builtin_extend_pointer (xh->value));
338   _Unwind_SetGR (context, __builtin_eh_return_data_regno (1),
339                  handler_switch_value);
340   _Unwind_SetIP (context, landing_pad);
341   return _URC_INSTALL_CONTEXT;
342 }
343
344 static void
345 __objc_exception_cleanup (_Unwind_Reason_Code code __attribute__((unused)),
346                           struct _Unwind_Exception *exc)
347 {
348   free (exc);
349 }
350
351 void
352 objc_exception_throw (id value)
353 {
354   struct ObjcException *header = calloc (1, sizeof (*header));
355   header->base.exception_class = __objc_exception_class;
356   header->base.exception_cleanup = __objc_exception_cleanup;
357   header->value = value;
358
359 #ifdef _GLIBCXX_SJLJ_EXCEPTIONS
360   _Unwind_SjLj_RaiseException (&header->base);
361 #else
362   _Unwind_RaiseException (&header->base);
363 #endif
364
365   /* Some sort of unwinding error.  */
366   abort ();
367 }