]>
Commit | Line | Data |
---|---|---|
e04a16fb | 1 | /* Handle exceptions for GNU compiler for the Java(TM) language. |
073681c5 | 2 | Copyright (C) 1997, 1998, 1999, 2000, 2002 Free Software Foundation, Inc. |
e04a16fb AG |
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 | ||
e04a16fb | 25 | #include "config.h" |
1f43f4b4 | 26 | #include "system.h" |
e04a16fb AG |
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" | |
b384405b | 34 | #include "function.h" |
e04a16fb AG |
35 | #include "except.h" |
36 | #include "java-except.h" | |
1f43f4b4 | 37 | #include "toplev.h" |
e04a16fb | 38 | |
df32d2ce KG |
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 *, | |
c8e7d2e6 | 42 | struct eh_range *)); |
df32d2ce | 43 | static void link_handler PARAMS ((struct eh_range *, struct eh_range *)); |
ae0a06c5 | 44 | static void check_start_handlers PARAMS ((struct eh_range *, int)); |
1f8f4a0b | 45 | static void free_eh_ranges PARAMS ((struct eh_range *range)); |
4bcde32e | 46 | |
e04a16fb AG |
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 | ||
e8b22dd1 | 63 | #if defined(DEBUG_JAVA_BINDING_LEVELS) |
b139fcfd AH |
64 | extern int binding_depth; |
65 | extern int is_class_level; | |
66 | extern int current_pc; | |
e8b22dd1 AH |
67 | extern void indent (); |
68 | ||
69 | #endif | |
70 | ||
e04a16fb AG |
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 * | |
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; | |
44d7502b | 86 | if (pc < child->end_pc) |
e04a16fb AG |
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 * | |
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 | ||
5a9e5c6f | 122 | /* Recursive helper routine for check_nested_ranges. */ |
e04a16fb | 123 | |
5a9e5c6f TT |
124 | static void |
125 | link_handler (range, outer) | |
126 | struct eh_range *range, *outer; | |
e04a16fb AG |
127 | { |
128 | struct eh_range **ptr; | |
5a9e5c6f TT |
129 | |
130 | if (range->start_pc == outer->start_pc && range->end_pc == outer->end_pc) | |
131 | { | |
99fd3aa5 | 132 | outer->handlers = chainon (outer->handlers, range->handlers); |
5a9e5c6f TT |
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; | |
44d7502b AH |
143 | { |
144 | struct eh_range **pr = &(outer->outer->first_child); | |
145 | while (*pr != outer) | |
146 | pr = &(*pr)->next_sibling; | |
147 | *pr = range; | |
148 | } | |
5a9e5c6f TT |
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) | |
e04a16fb | 155 | { |
5a9e5c6f | 156 | struct eh_range *h |
1f8f4a0b | 157 | = (struct eh_range *) xmalloc (sizeof (struct eh_range)); |
5a9e5c6f TT |
158 | if (range->start_pc < outer->start_pc) |
159 | { | |
160 | h->start_pc = range->start_pc; | |
161 | h->end_pc = outer->start_pc; | |
162 | range->start_pc = outer->start_pc; | |
163 | } | |
164 | else | |
165 | { | |
166 | h->start_pc = outer->end_pc; | |
167 | h->end_pc = range->end_pc; | |
168 | range->end_pc = outer->end_pc; | |
169 | } | |
170 | h->first_child = NULL; | |
171 | h->outer = NULL; | |
172 | h->handlers = build_tree_list (TREE_PURPOSE (range->handlers), | |
173 | TREE_VALUE (range->handlers)); | |
174 | h->next_sibling = NULL; | |
b139fcfd | 175 | h->expanded = 0; |
5a9e5c6f TT |
176 | /* Restart both from the top to avoid having to make this |
177 | function smart about reentrancy. */ | |
178 | link_handler (h, &whole_range); | |
179 | link_handler (range, &whole_range); | |
180 | return; | |
e04a16fb | 181 | } |
5a9e5c6f | 182 | |
e04a16fb AG |
183 | ptr = &outer->first_child; |
184 | for (;; ptr = &(*ptr)->next_sibling) | |
185 | { | |
5a9e5c6f | 186 | if (*ptr == NULL || range->end_pc <= (*ptr)->start_pc) |
e04a16fb | 187 | { |
5a9e5c6f TT |
188 | range->next_sibling = *ptr; |
189 | range->first_child = NULL; | |
190 | range->outer = outer; | |
191 | *ptr = range; | |
192 | return; | |
193 | } | |
194 | else if (range->start_pc < (*ptr)->end_pc) | |
195 | { | |
196 | link_handler (range, *ptr); | |
197 | return; | |
e04a16fb | 198 | } |
e04a16fb AG |
199 | /* end_pc > (*ptr)->start_pc && start_pc >= (*ptr)->end_pc. */ |
200 | } | |
201 | } | |
202 | ||
5a9e5c6f TT |
203 | /* The first pass of exception range processing (calling add_handler) |
204 | constructs a linked list of exception ranges. We turn this into | |
205 | the data structure expected by the rest of the code, and also | |
206 | ensure that exception ranges are properly nested. */ | |
207 | ||
208 | void | |
209 | handle_nested_ranges () | |
210 | { | |
211 | struct eh_range *ptr, *next; | |
212 | ||
213 | ptr = whole_range.first_child; | |
214 | whole_range.first_child = NULL; | |
215 | for (; ptr; ptr = next) | |
216 | { | |
217 | next = ptr->next_sibling; | |
218 | ptr->next_sibling = NULL; | |
219 | link_handler (ptr, &whole_range); | |
220 | } | |
221 | } | |
222 | ||
1f8f4a0b MM |
223 | /* Free RANGE as well as its children and siblings. */ |
224 | ||
225 | static void | |
226 | free_eh_ranges (range) | |
227 | struct eh_range *range; | |
228 | { | |
229 | while (range) | |
230 | { | |
231 | struct eh_range *next = range->next_sibling; | |
232 | free_eh_ranges (range->first_child); | |
1a2ebe6d APB |
233 | if (range != &whole_range) |
234 | free (range); | |
1f8f4a0b MM |
235 | range = next; |
236 | } | |
237 | } | |
5a9e5c6f | 238 | |
e04a16fb AG |
239 | /* Called to re-initialize the exception machinery for a new method. */ |
240 | ||
241 | void | |
242 | method_init_exceptions () | |
243 | { | |
1f8f4a0b | 244 | free_eh_ranges (&whole_range); |
e04a16fb AG |
245 | whole_range.start_pc = 0; |
246 | whole_range.end_pc = DECL_CODE_LENGTH (current_function_decl) + 1; | |
247 | whole_range.outer = NULL; | |
248 | whole_range.first_child = NULL; | |
249 | whole_range.next_sibling = NULL; | |
250 | cache_range_start = 0xFFFFFF; | |
e04a16fb AG |
251 | } |
252 | ||
5a9e5c6f TT |
253 | /* Add an exception range. If we already have an exception range |
254 | which has the same handler and label, and the new range overlaps | |
255 | that one, then we simply extend the existing range. Some bytecode | |
256 | obfuscators generate seemingly nonoverlapping exception ranges | |
257 | which, when coalesced, do in fact nest correctly. | |
258 | ||
259 | This constructs an ordinary linked list which check_nested_ranges() | |
260 | later turns into the data structure we actually want. | |
261 | ||
262 | We expect the input to come in order of increasing START_PC. This | |
263 | function doesn't attempt to detect the case where two previously | |
264 | added disjoint ranges could be coalesced by a new range; that is | |
265 | what the sorting counteracts. */ | |
266 | ||
267 | void | |
e04a16fb AG |
268 | add_handler (start_pc, end_pc, handler, type) |
269 | int start_pc, end_pc; | |
270 | tree handler; | |
271 | tree type; | |
272 | { | |
5a9e5c6f TT |
273 | struct eh_range *ptr, *prev = NULL, *h; |
274 | ||
275 | for (ptr = whole_range.first_child; ptr; ptr = ptr->next_sibling) | |
276 | { | |
277 | if (start_pc >= ptr->start_pc | |
278 | && start_pc <= ptr->end_pc | |
279 | && TREE_PURPOSE (ptr->handlers) == type | |
280 | && TREE_VALUE (ptr->handlers) == handler) | |
281 | { | |
282 | /* Already found an overlapping range, so coalesce. */ | |
283 | ptr->end_pc = MAX (ptr->end_pc, end_pc); | |
284 | return; | |
285 | } | |
286 | prev = ptr; | |
287 | } | |
288 | ||
1f8f4a0b | 289 | h = (struct eh_range *) xmalloc (sizeof (struct eh_range)); |
5a9e5c6f TT |
290 | h->start_pc = start_pc; |
291 | h->end_pc = end_pc; | |
292 | h->first_child = NULL; | |
293 | h->outer = NULL; | |
294 | h->handlers = build_tree_list (type, handler); | |
295 | h->next_sibling = NULL; | |
e8b22dd1 | 296 | h->expanded = 0; |
5a9e5c6f TT |
297 | |
298 | if (prev == NULL) | |
299 | whole_range.first_child = h; | |
300 | else | |
301 | prev->next_sibling = h; | |
e04a16fb AG |
302 | } |
303 | ||
304 | ||
305 | /* if there are any handlers for this range, issue start of region */ | |
4bcde32e | 306 | static void |
e04a16fb | 307 | expand_start_java_handler (range) |
e8b22dd1 | 308 | struct eh_range *range; |
e04a16fb | 309 | { |
e8b22dd1 AH |
310 | #if defined(DEBUG_JAVA_BINDING_LEVELS) |
311 | indent (); | |
312 | fprintf (stderr, "expand start handler pc %d --> %d\n", | |
313 | current_pc, range->end_pc); | |
314 | #endif /* defined(DEBUG_JAVA_BINDING_LEVELS) */ | |
315 | range->expanded = 1; | |
e04a16fb AG |
316 | expand_eh_region_start (); |
317 | } | |
318 | ||
e4de5a10 PB |
319 | tree |
320 | prepare_eh_table_type (type) | |
321 | tree type; | |
322 | { | |
323 | tree exp; | |
324 | ||
325 | /* The "type" (metch_info) in a (Java) exception table is one: | |
326 | * a) NULL - meaning match any type in a try-finally. | |
327 | * b) a pointer to a (ccmpiled) class (low-order bit 0). | |
328 | * c) a pointer to the Utf8Const name of the class, plus one | |
329 | * (which yields a value with low-order bit 1). */ | |
330 | ||
e4de5a10 | 331 | if (type == NULL_TREE) |
52a11cbf | 332 | exp = NULL_TREE; |
e4de5a10 PB |
333 | else if (is_compiled_class (type)) |
334 | exp = build_class_ref (type); | |
335 | else | |
336 | exp = fold (build | |
337 | (PLUS_EXPR, ptr_type_node, | |
338 | build_utf8_ref (build_internal_class_name (type)), | |
339 | size_one_node)); | |
e4de5a10 PB |
340 | return exp; |
341 | } | |
342 | ||
52a11cbf RH |
343 | |
344 | /* Build a reference to the jthrowable object being carried in the | |
345 | exception header. */ | |
346 | ||
347 | tree | |
348 | build_exception_object_ref (type) | |
349 | tree type; | |
350 | { | |
351 | tree obj; | |
352 | ||
353 | /* Java only passes object via pointer and doesn't require adjusting. | |
354 | The java object is immediately before the generic exception header. */ | |
355 | obj = build (EXC_PTR_EXPR, build_pointer_type (type)); | |
356 | obj = build (MINUS_EXPR, TREE_TYPE (obj), obj, | |
357 | TYPE_SIZE_UNIT (TREE_TYPE (obj))); | |
358 | obj = build1 (INDIRECT_REF, type, obj); | |
359 | ||
360 | return obj; | |
361 | } | |
362 | ||
363 | /* If there are any handlers for this range, isssue end of range, | |
e04a16fb | 364 | and then all handler blocks */ |
4bcde32e | 365 | static void |
e04a16fb AG |
366 | expand_end_java_handler (range) |
367 | struct eh_range *range; | |
e8b22dd1 | 368 | { |
e04a16fb | 369 | tree handler = range->handlers; |
e8b22dd1 | 370 | force_poplevels (range->start_pc); |
e04a16fb AG |
371 | expand_start_all_catch (); |
372 | for ( ; handler != NULL_TREE; handler = TREE_CHAIN (handler)) | |
373 | { | |
073681c5 TT |
374 | /* For bytecode we treat exceptions a little unusually. A |
375 | `finally' clause looks like an ordinary exception handler for | |
376 | Throwable. The reason for this is that the bytecode has | |
377 | already expanded the finally logic, and we would have to do | |
378 | extra (and difficult) work to get this to look like a | |
379 | gcc-style finally clause. */ | |
380 | tree type = TREE_PURPOSE (handler); | |
381 | if (type == NULL) | |
382 | type = throwable_type_node; | |
383 | ||
384 | expand_start_catch (type); | |
e04a16fb | 385 | expand_goto (TREE_VALUE (handler)); |
52a11cbf | 386 | expand_end_catch (); |
e04a16fb AG |
387 | } |
388 | expand_end_all_catch (); | |
e8b22dd1 AH |
389 | #if defined(DEBUG_JAVA_BINDING_LEVELS) |
390 | indent (); | |
391 | fprintf (stderr, "expand end handler pc %d <-- %d\n", | |
392 | current_pc, range->start_pc); | |
393 | #endif /* defined(DEBUG_JAVA_BINDING_LEVELS) */ | |
e04a16fb AG |
394 | } |
395 | ||
396 | /* Recursive helper routine for maybe_start_handlers. */ | |
397 | ||
398 | static void | |
399 | check_start_handlers (range, pc) | |
400 | struct eh_range *range; | |
401 | int pc; | |
402 | { | |
403 | if (range != NULL_EH_RANGE && range->start_pc == pc) | |
404 | { | |
405 | check_start_handlers (range->outer, pc); | |
e8b22dd1 AH |
406 | if (!range->expanded) |
407 | expand_start_java_handler (range); | |
e04a16fb AG |
408 | } |
409 | } | |
410 | ||
e04a16fb | 411 | |
e8b22dd1 AH |
412 | static struct eh_range *current_range; |
413 | ||
414 | /* Emit any start-of-try-range starting at start_pc and ending after | |
415 | end_pc. */ | |
e04a16fb AG |
416 | |
417 | void | |
e8b22dd1 AH |
418 | maybe_start_try (start_pc, end_pc) |
419 | int start_pc; | |
420 | int end_pc; | |
e04a16fb | 421 | { |
e8b22dd1 | 422 | struct eh_range *range; |
e04a16fb AG |
423 | if (! doing_eh (1)) |
424 | return; | |
425 | ||
e8b22dd1 AH |
426 | range = find_handler (start_pc); |
427 | while (range != NULL_EH_RANGE && range->start_pc == start_pc | |
428 | && range->end_pc < end_pc) | |
429 | range = range->outer; | |
430 | ||
431 | current_range = range; | |
ef86eabb | 432 | check_start_handlers (range, start_pc); |
e04a16fb AG |
433 | } |
434 | ||
e8b22dd1 AH |
435 | /* Emit any end-of-try-range ending at end_pc and starting before |
436 | start_pc. */ | |
e04a16fb AG |
437 | |
438 | void | |
e8b22dd1 AH |
439 | maybe_end_try (start_pc, end_pc) |
440 | int start_pc; | |
441 | int end_pc; | |
e04a16fb AG |
442 | { |
443 | if (! doing_eh (1)) | |
444 | return; | |
445 | ||
e8b22dd1 AH |
446 | while (current_range != NULL_EH_RANGE && current_range->end_pc <= end_pc |
447 | && current_range->start_pc >= start_pc) | |
e04a16fb AG |
448 | { |
449 | expand_end_java_handler (current_range); | |
450 | current_range = current_range->outer; | |
451 | } | |
452 | } |