xref: /openbsd-src/gnu/usr.bin/gcc/gcc/java/except.c (revision c87b03e512fc05ed6e0222f6fb0ae86264b1d05b)
1 /* Handle exceptions for GNU compiler for the Java(TM) language.
2    Copyright (C) 1997, 1998, 1999, 2000, 2002 Free Software Foundation, Inc.
3 
4 This file is part of GNU CC.
5 
6 GNU CC is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU CC is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU CC; 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 Java and all Java-based marks are trademarks or registered trademarks
22 of Sun Microsystems, Inc. in the United States and other countries.
23 The Free Software Foundation is independent of Sun Microsystems, Inc.  */
24 
25 #include "config.h"
26 #include "system.h"
27 #include "tree.h"
28 #include "real.h"
29 #include "rtl.h"
30 #include "java-tree.h"
31 #include "javaop.h"
32 #include "java-opcodes.h"
33 #include "jcf.h"
34 #include "function.h"
35 #include "except.h"
36 #include "java-except.h"
37 #include "toplev.h"
38 
39 static void expand_start_java_handler PARAMS ((struct eh_range *));
40 static void expand_end_java_handler PARAMS ((struct eh_range *));
41 static struct eh_range *find_handler_in_range PARAMS ((int, struct eh_range *,
42 						      struct eh_range *));
43 static void link_handler PARAMS ((struct eh_range *, struct eh_range *));
44 static void check_start_handlers PARAMS ((struct eh_range *, int));
45 static void free_eh_ranges PARAMS ((struct eh_range *range));
46 
47 struct eh_range *current_method_handlers;
48 
49 struct eh_range *current_try_block = NULL;
50 
51 struct eh_range *eh_range_freelist = NULL;
52 
53 /* These variables are used to speed up find_handler. */
54 
55 static int cache_range_start, cache_range_end;
56 static struct eh_range *cache_range;
57 static struct eh_range *cache_next_child;
58 
59 /* A dummy range that represents the entire method. */
60 
61 struct eh_range whole_range;
62 
63 #if defined(DEBUG_JAVA_BINDING_LEVELS)
64 extern int binding_depth;
65 extern int is_class_level;
66 extern int current_pc;
67 extern void indent ();
68 
69 #endif
70 
71 /* Search for the most specific eh_range containing PC.
72    Assume PC is within RANGE.
73    CHILD is a list of children of RANGE such that any
74    previous children have end_pc values that are too low. */
75 
76 static struct eh_range *
find_handler_in_range(pc,range,child)77 find_handler_in_range (pc, range, child)
78      int pc;
79      struct eh_range *range;
80      register struct eh_range *child;
81 {
82   for (; child != NULL;  child = child->next_sibling)
83     {
84       if (pc < child->start_pc)
85 	break;
86       if (pc < child->end_pc)
87 	return find_handler_in_range (pc, child, child->first_child);
88     }
89   cache_range = range;
90   cache_range_start = pc;
91   cache_next_child = child;
92   cache_range_end = child == NULL ? range->end_pc : child->start_pc;
93   return range;
94 }
95 
96 /* Find the inner-most handler that contains PC. */
97 
98 struct eh_range *
find_handler(pc)99 find_handler (pc)
100      int pc;
101 {
102   struct eh_range *h;
103   if (pc >= cache_range_start)
104     {
105       h = cache_range;
106       if (pc < cache_range_end)
107 	return h;
108       while (pc >= h->end_pc)
109 	{
110 	  cache_next_child = h->next_sibling;
111 	  h = h->outer;
112 	}
113     }
114   else
115     {
116       h = &whole_range;
117       cache_next_child = h->first_child;
118     }
119   return find_handler_in_range (pc, h, cache_next_child);
120 }
121 
122 /* Recursive helper routine for check_nested_ranges. */
123 
124 static void
link_handler(range,outer)125 link_handler (range, outer)
126      struct eh_range *range, *outer;
127 {
128   struct eh_range **ptr;
129 
130   if (range->start_pc == outer->start_pc && range->end_pc == outer->end_pc)
131     {
132       outer->handlers = chainon (outer->handlers, range->handlers);
133       return;
134     }
135 
136   /* If the new range completely encloses the `outer' range, then insert it
137      between the outer range and its parent.  */
138   if (range->start_pc <= outer->start_pc && range->end_pc >= outer->end_pc)
139     {
140       range->outer = outer->outer;
141       range->next_sibling = NULL;
142       range->first_child = outer;
143       {
144 	struct eh_range **pr = &(outer->outer->first_child);
145 	while (*pr != outer)
146 	  pr = &(*pr)->next_sibling;
147 	*pr = range;
148       }
149       outer->outer = range;
150       return;
151     }
152 
153   /* Handle overlapping ranges by splitting the new range.  */
154   if (range->start_pc < outer->start_pc || range->end_pc > outer->end_pc)
155     {
156       struct eh_range *h = xmalloc (sizeof (struct eh_range));
157       if (range->start_pc < outer->start_pc)
158 	{
159 	  h->start_pc = range->start_pc;
160 	  h->end_pc = outer->start_pc;
161 	  range->start_pc = outer->start_pc;
162 	}
163       else
164 	{
165 	  h->start_pc = outer->end_pc;
166 	  h->end_pc = range->end_pc;
167 	  range->end_pc = outer->end_pc;
168 	}
169       h->first_child = NULL;
170       h->outer = NULL;
171       h->handlers = build_tree_list (TREE_PURPOSE (range->handlers),
172 				     TREE_VALUE (range->handlers));
173       h->next_sibling = NULL;
174       h->expanded = 0;
175       /* Restart both from the top to avoid having to make this
176 	 function smart about reentrancy.  */
177       link_handler (h, &whole_range);
178       link_handler (range, &whole_range);
179       return;
180     }
181 
182   ptr = &outer->first_child;
183   for (;; ptr = &(*ptr)->next_sibling)
184     {
185       if (*ptr == NULL || range->end_pc <= (*ptr)->start_pc)
186 	{
187 	  range->next_sibling = *ptr;
188 	  range->first_child = NULL;
189 	  range->outer = outer;
190 	  *ptr = range;
191 	  return;
192 	}
193       else if (range->start_pc < (*ptr)->end_pc)
194 	{
195 	  link_handler (range, *ptr);
196 	  return;
197 	}
198       /* end_pc > (*ptr)->start_pc && start_pc >= (*ptr)->end_pc. */
199     }
200 }
201 
202 /* The first pass of exception range processing (calling add_handler)
203    constructs a linked list of exception ranges.  We turn this into
204    the data structure expected by the rest of the code, and also
205    ensure that exception ranges are properly nested.  */
206 
207 void
handle_nested_ranges()208 handle_nested_ranges ()
209 {
210   struct eh_range *ptr, *next;
211 
212   ptr = whole_range.first_child;
213   whole_range.first_child = NULL;
214   for (; ptr; ptr = next)
215     {
216       next = ptr->next_sibling;
217       ptr->next_sibling = NULL;
218       link_handler (ptr, &whole_range);
219     }
220 }
221 
222 /* Free RANGE as well as its children and siblings.  */
223 
224 static void
free_eh_ranges(range)225 free_eh_ranges (range)
226      struct eh_range *range;
227 {
228   while (range)
229     {
230       struct eh_range *next = range->next_sibling;
231       free_eh_ranges (range->first_child);
232       if (range != &whole_range)
233 	free (range);
234       range = next;
235     }
236 }
237 
238 /* Called to re-initialize the exception machinery for a new method. */
239 
240 void
method_init_exceptions()241 method_init_exceptions ()
242 {
243   free_eh_ranges (&whole_range);
244   whole_range.start_pc = 0;
245   whole_range.end_pc = DECL_CODE_LENGTH (current_function_decl) + 1;
246   whole_range.outer = NULL;
247   whole_range.first_child = NULL;
248   whole_range.next_sibling = NULL;
249   cache_range_start = 0xFFFFFF;
250 }
251 
252 /* Add an exception range.  If we already have an exception range
253    which has the same handler and label, and the new range overlaps
254    that one, then we simply extend the existing range.  Some bytecode
255    obfuscators generate seemingly nonoverlapping exception ranges
256    which, when coalesced, do in fact nest correctly.
257 
258    This constructs an ordinary linked list which check_nested_ranges()
259    later turns into the data structure we actually want.
260 
261    We expect the input to come in order of increasing START_PC.  This
262    function doesn't attempt to detect the case where two previously
263    added disjoint ranges could be coalesced by a new range; that is
264    what the sorting counteracts.  */
265 
266 void
add_handler(start_pc,end_pc,handler,type)267 add_handler (start_pc, end_pc, handler, type)
268      int start_pc, end_pc;
269      tree handler;
270      tree type;
271 {
272   struct eh_range *ptr, *prev = NULL, *h;
273 
274   for (ptr = whole_range.first_child; ptr; ptr = ptr->next_sibling)
275     {
276       if (start_pc >= ptr->start_pc
277 	  && start_pc <= ptr->end_pc
278 	  && TREE_PURPOSE (ptr->handlers) == type
279 	  && TREE_VALUE (ptr->handlers) == handler)
280 	{
281 	  /* Already found an overlapping range, so coalesce.  */
282 	  ptr->end_pc = MAX (ptr->end_pc, end_pc);
283 	  return;
284 	}
285       prev = ptr;
286     }
287 
288   h = xmalloc (sizeof (struct eh_range));
289   h->start_pc = start_pc;
290   h->end_pc = end_pc;
291   h->first_child = NULL;
292   h->outer = NULL;
293   h->handlers = build_tree_list (type, handler);
294   h->next_sibling = NULL;
295   h->expanded = 0;
296 
297   if (prev == NULL)
298     whole_range.first_child = h;
299   else
300     prev->next_sibling = h;
301 }
302 
303 
304 /* if there are any handlers for this range, issue start of region */
305 static void
expand_start_java_handler(range)306 expand_start_java_handler (range)
307   struct eh_range *range;
308 {
309 #if defined(DEBUG_JAVA_BINDING_LEVELS)
310   indent ();
311   fprintf (stderr, "expand start handler pc %d --> %d\n",
312 	   current_pc, range->end_pc);
313 #endif /* defined(DEBUG_JAVA_BINDING_LEVELS) */
314   range->expanded = 1;
315   expand_eh_region_start ();
316 }
317 
318 tree
prepare_eh_table_type(type)319 prepare_eh_table_type (type)
320     tree type;
321 {
322   tree exp;
323 
324   /* The "type" (metch_info) in a (Java) exception table is one:
325    * a) NULL - meaning match any type in a try-finally.
326    * b) a pointer to a (ccmpiled) class (low-order bit 0).
327    * c) a pointer to the Utf8Const name of the class, plus one
328    * (which yields a value with low-order bit 1). */
329 
330   if (type == NULL_TREE)
331     exp = NULL_TREE;
332   else if (is_compiled_class (type))
333     exp = build_class_ref (type);
334   else
335     exp = fold (build
336 		(PLUS_EXPR, ptr_type_node,
337 		 build_utf8_ref (build_internal_class_name (type)),
338 		 size_one_node));
339   return exp;
340 }
341 
342 
343 /* Build a reference to the jthrowable object being carried in the
344    exception header.  */
345 
346 tree
build_exception_object_ref(type)347 build_exception_object_ref (type)
348      tree type;
349 {
350   tree obj;
351 
352   /* Java only passes object via pointer and doesn't require adjusting.
353      The java object is immediately before the generic exception header.  */
354   obj = build (EXC_PTR_EXPR, build_pointer_type (type));
355   obj = build (MINUS_EXPR, TREE_TYPE (obj), obj,
356 	       TYPE_SIZE_UNIT (TREE_TYPE (obj)));
357   obj = build1 (INDIRECT_REF, type, obj);
358 
359   return obj;
360 }
361 
362 /* If there are any handlers for this range, isssue end of range,
363    and then all handler blocks */
364 static void
expand_end_java_handler(range)365 expand_end_java_handler (range)
366      struct eh_range *range;
367 {
368   tree handler = range->handlers;
369   force_poplevels (range->start_pc);
370   expand_start_all_catch ();
371   for ( ; handler != NULL_TREE; handler = TREE_CHAIN (handler))
372     {
373       /* For bytecode we treat exceptions a little unusually.  A
374 	 `finally' clause looks like an ordinary exception handler for
375 	 Throwable.  The reason for this is that the bytecode has
376 	 already expanded the finally logic, and we would have to do
377 	 extra (and difficult) work to get this to look like a
378 	 gcc-style finally clause.  */
379       tree type = TREE_PURPOSE (handler);
380       if (type == NULL)
381 	type = throwable_type_node;
382 
383       expand_start_catch (type);
384       expand_goto (TREE_VALUE (handler));
385       expand_end_catch ();
386     }
387   expand_end_all_catch ();
388 #if defined(DEBUG_JAVA_BINDING_LEVELS)
389   indent ();
390   fprintf (stderr, "expand end handler pc %d <-- %d\n",
391 	   current_pc, range->start_pc);
392 #endif /* defined(DEBUG_JAVA_BINDING_LEVELS) */
393 }
394 
395 /* Recursive helper routine for maybe_start_handlers. */
396 
397 static void
check_start_handlers(range,pc)398 check_start_handlers (range, pc)
399      struct eh_range *range;
400      int pc;
401 {
402   if (range != NULL_EH_RANGE && range->start_pc == pc)
403     {
404       check_start_handlers (range->outer, pc);
405       if (!range->expanded)
406 	expand_start_java_handler (range);
407     }
408 }
409 
410 
411 static struct eh_range *current_range;
412 
413 /* Emit any start-of-try-range starting at start_pc and ending after
414    end_pc. */
415 
416 void
maybe_start_try(start_pc,end_pc)417 maybe_start_try (start_pc, end_pc)
418      int start_pc;
419      int end_pc;
420 {
421   struct eh_range *range;
422   if (! doing_eh (1))
423     return;
424 
425   range = find_handler (start_pc);
426   while (range != NULL_EH_RANGE && range->start_pc == start_pc
427 	 && range->end_pc < end_pc)
428     range = range->outer;
429 
430   current_range = range;
431   check_start_handlers (range, start_pc);
432 }
433 
434 /* Emit any end-of-try-range ending at end_pc and starting before
435    start_pc. */
436 
437 void
maybe_end_try(start_pc,end_pc)438 maybe_end_try (start_pc, end_pc)
439      int start_pc;
440      int end_pc;
441 {
442   if (! doing_eh (1))
443     return;
444 
445   while (current_range != NULL_EH_RANGE && current_range->end_pc <= end_pc
446 	 && current_range->start_pc >= start_pc)
447     {
448       expand_end_java_handler (current_range);
449       current_range = current_range->outer;
450     }
451 }
452