OSDN Git Service

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