OSDN Git Service

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