Add objc to the gcc-4.1.2.
[dragonfly.git] / contrib / gcc-4.1 / 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, 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, 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 = 0, saw_handler;
169   void *return_object;
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   if (saw_cleanup == 0)
339     {
340       return_object = xh->value;
341       if (!(actions & _UA_SEARCH_PHASE))
342         _Unwind_DeleteException(&xh->base);
343     }
344   
345   _Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
346                  __builtin_extend_pointer (saw_cleanup ? xh : return_object));
347   _Unwind_SetGR (context, __builtin_eh_return_data_regno (1),
348                  handler_switch_value);
349   _Unwind_SetIP (context, landing_pad);
350   return _URC_INSTALL_CONTEXT;
351 }
352
353 static void
354 __objc_exception_cleanup (_Unwind_Reason_Code code __attribute__((unused)),
355                           struct _Unwind_Exception *exc)
356 {
357   free (exc);
358 }
359
360 void
361 objc_exception_throw (id value)
362 {
363   struct ObjcException *header = calloc (1, sizeof (*header));
364   header->base.exception_class = __objc_exception_class;
365   header->base.exception_cleanup = __objc_exception_cleanup;
366   header->value = value;
367
368 #ifdef SJLJ_EXCEPTIONS
369   _Unwind_SjLj_RaiseException (&header->base);
370 #else
371   _Unwind_RaiseException (&header->base);
372 #endif
373
374   /* Some sort of unwinding error.  */
375   abort ();
376 }