]>
Commit | Line | Data |
---|---|---|
e04a16fb | 1 | /* Handle exceptions for GNU compiler for the Java(TM) language. |
df32d2ce | 2 | Copyright (C) 1997, 98-99, 2000 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" | |
37 | #include "eh-common.h" | |
1f43f4b4 | 38 | #include "toplev.h" |
e04a16fb | 39 | |
df32d2ce KG |
40 | static void expand_start_java_handler PARAMS ((struct eh_range *)); |
41 | static void expand_end_java_handler PARAMS ((struct eh_range *)); | |
42 | static struct eh_range *find_handler_in_range PARAMS ((int, struct eh_range *, | |
c8e7d2e6 | 43 | struct eh_range *)); |
df32d2ce KG |
44 | static void link_handler PARAMS ((struct eh_range *, struct eh_range *)); |
45 | static void check_start_handlers PARAMS ((struct eh_range *, int)); | |
4bcde32e | 46 | |
e04a16fb AG |
47 | extern struct obstack permanent_obstack; |
48 | ||
49 | struct eh_range *current_method_handlers; | |
50 | ||
51 | struct eh_range *current_try_block = NULL; | |
52 | ||
53 | struct eh_range *eh_range_freelist = NULL; | |
54 | ||
55 | /* These variables are used to speed up find_handler. */ | |
56 | ||
57 | static int cache_range_start, cache_range_end; | |
58 | static struct eh_range *cache_range; | |
59 | static struct eh_range *cache_next_child; | |
60 | ||
61 | /* A dummy range that represents the entire method. */ | |
62 | ||
63 | struct eh_range whole_range; | |
64 | ||
65 | /* Search for the most specific eh_range containing PC. | |
66 | Assume PC is within RANGE. | |
67 | CHILD is a list of children of RANGE such that any | |
68 | previous children have end_pc values that are too low. */ | |
69 | ||
70 | static struct eh_range * | |
71 | find_handler_in_range (pc, range, child) | |
72 | int pc; | |
73 | struct eh_range *range; | |
74 | register struct eh_range *child; | |
75 | { | |
76 | for (; child != NULL; child = child->next_sibling) | |
77 | { | |
78 | if (pc < child->start_pc) | |
79 | break; | |
44d7502b | 80 | if (pc < child->end_pc) |
e04a16fb AG |
81 | return find_handler_in_range (pc, child, child->first_child); |
82 | } | |
83 | cache_range = range; | |
84 | cache_range_start = pc; | |
85 | cache_next_child = child; | |
86 | cache_range_end = child == NULL ? range->end_pc : child->start_pc; | |
87 | return range; | |
88 | } | |
89 | ||
90 | /* Find the inner-most handler that contains PC. */ | |
91 | ||
92 | struct eh_range * | |
93 | find_handler (pc) | |
94 | int pc; | |
95 | { | |
96 | struct eh_range *h; | |
97 | if (pc >= cache_range_start) | |
98 | { | |
99 | h = cache_range; | |
100 | if (pc < cache_range_end) | |
101 | return h; | |
102 | while (pc >= h->end_pc) | |
103 | { | |
104 | cache_next_child = h->next_sibling; | |
105 | h = h->outer; | |
106 | } | |
107 | } | |
108 | else | |
109 | { | |
110 | h = &whole_range; | |
111 | cache_next_child = h->first_child; | |
112 | } | |
113 | return find_handler_in_range (pc, h, cache_next_child); | |
114 | } | |
115 | ||
5a9e5c6f | 116 | /* Recursive helper routine for check_nested_ranges. */ |
e04a16fb | 117 | |
5a9e5c6f TT |
118 | static void |
119 | link_handler (range, outer) | |
120 | struct eh_range *range, *outer; | |
e04a16fb AG |
121 | { |
122 | struct eh_range **ptr; | |
5a9e5c6f TT |
123 | |
124 | if (range->start_pc == outer->start_pc && range->end_pc == outer->end_pc) | |
125 | { | |
99fd3aa5 | 126 | outer->handlers = chainon (outer->handlers, range->handlers); |
5a9e5c6f TT |
127 | return; |
128 | } | |
129 | ||
130 | /* If the new range completely encloses the `outer' range, then insert it | |
131 | between the outer range and its parent. */ | |
132 | if (range->start_pc <= outer->start_pc && range->end_pc >= outer->end_pc) | |
133 | { | |
134 | range->outer = outer->outer; | |
135 | range->next_sibling = NULL; | |
136 | range->first_child = outer; | |
44d7502b AH |
137 | { |
138 | struct eh_range **pr = &(outer->outer->first_child); | |
139 | while (*pr != outer) | |
140 | pr = &(*pr)->next_sibling; | |
141 | *pr = range; | |
142 | } | |
5a9e5c6f TT |
143 | outer->outer = range; |
144 | return; | |
145 | } | |
146 | ||
147 | /* Handle overlapping ranges by splitting the new range. */ | |
148 | if (range->start_pc < outer->start_pc || range->end_pc > outer->end_pc) | |
e04a16fb | 149 | { |
5a9e5c6f TT |
150 | struct eh_range *h |
151 | = (struct eh_range *) oballoc (sizeof (struct eh_range)); | |
152 | if (range->start_pc < outer->start_pc) | |
153 | { | |
154 | h->start_pc = range->start_pc; | |
155 | h->end_pc = outer->start_pc; | |
156 | range->start_pc = outer->start_pc; | |
157 | } | |
158 | else | |
159 | { | |
160 | h->start_pc = outer->end_pc; | |
161 | h->end_pc = range->end_pc; | |
162 | range->end_pc = outer->end_pc; | |
163 | } | |
164 | h->first_child = NULL; | |
165 | h->outer = NULL; | |
166 | h->handlers = build_tree_list (TREE_PURPOSE (range->handlers), | |
167 | TREE_VALUE (range->handlers)); | |
168 | h->next_sibling = NULL; | |
169 | /* Restart both from the top to avoid having to make this | |
170 | function smart about reentrancy. */ | |
171 | link_handler (h, &whole_range); | |
172 | link_handler (range, &whole_range); | |
173 | return; | |
e04a16fb | 174 | } |
5a9e5c6f | 175 | |
e04a16fb AG |
176 | ptr = &outer->first_child; |
177 | for (;; ptr = &(*ptr)->next_sibling) | |
178 | { | |
5a9e5c6f | 179 | if (*ptr == NULL || range->end_pc <= (*ptr)->start_pc) |
e04a16fb | 180 | { |
5a9e5c6f TT |
181 | range->next_sibling = *ptr; |
182 | range->first_child = NULL; | |
183 | range->outer = outer; | |
184 | *ptr = range; | |
185 | return; | |
186 | } | |
187 | else if (range->start_pc < (*ptr)->end_pc) | |
188 | { | |
189 | link_handler (range, *ptr); | |
190 | return; | |
e04a16fb | 191 | } |
e04a16fb AG |
192 | /* end_pc > (*ptr)->start_pc && start_pc >= (*ptr)->end_pc. */ |
193 | } | |
194 | } | |
195 | ||
5a9e5c6f TT |
196 | /* The first pass of exception range processing (calling add_handler) |
197 | constructs a linked list of exception ranges. We turn this into | |
198 | the data structure expected by the rest of the code, and also | |
199 | ensure that exception ranges are properly nested. */ | |
200 | ||
201 | void | |
202 | handle_nested_ranges () | |
203 | { | |
204 | struct eh_range *ptr, *next; | |
205 | ||
206 | ptr = whole_range.first_child; | |
207 | whole_range.first_child = NULL; | |
208 | for (; ptr; ptr = next) | |
209 | { | |
210 | next = ptr->next_sibling; | |
211 | ptr->next_sibling = NULL; | |
212 | link_handler (ptr, &whole_range); | |
213 | } | |
214 | } | |
215 | ||
216 | ||
e04a16fb AG |
217 | /* Called to re-initialize the exception machinery for a new method. */ |
218 | ||
219 | void | |
220 | method_init_exceptions () | |
221 | { | |
222 | whole_range.start_pc = 0; | |
223 | whole_range.end_pc = DECL_CODE_LENGTH (current_function_decl) + 1; | |
224 | whole_range.outer = NULL; | |
225 | whole_range.first_child = NULL; | |
226 | whole_range.next_sibling = NULL; | |
227 | cache_range_start = 0xFFFFFF; | |
e4de5a10 PB |
228 | java_set_exception_lang_code (); |
229 | } | |
230 | ||
231 | void | |
232 | java_set_exception_lang_code () | |
233 | { | |
e04a16fb AG |
234 | set_exception_lang_code (EH_LANG_Java); |
235 | set_exception_version_code (1); | |
236 | } | |
237 | ||
5a9e5c6f TT |
238 | /* Add an exception range. If we already have an exception range |
239 | which has the same handler and label, and the new range overlaps | |
240 | that one, then we simply extend the existing range. Some bytecode | |
241 | obfuscators generate seemingly nonoverlapping exception ranges | |
242 | which, when coalesced, do in fact nest correctly. | |
243 | ||
244 | This constructs an ordinary linked list which check_nested_ranges() | |
245 | later turns into the data structure we actually want. | |
246 | ||
247 | We expect the input to come in order of increasing START_PC. This | |
248 | function doesn't attempt to detect the case where two previously | |
249 | added disjoint ranges could be coalesced by a new range; that is | |
250 | what the sorting counteracts. */ | |
251 | ||
252 | void | |
e04a16fb AG |
253 | add_handler (start_pc, end_pc, handler, type) |
254 | int start_pc, end_pc; | |
255 | tree handler; | |
256 | tree type; | |
257 | { | |
5a9e5c6f TT |
258 | struct eh_range *ptr, *prev = NULL, *h; |
259 | ||
260 | for (ptr = whole_range.first_child; ptr; ptr = ptr->next_sibling) | |
261 | { | |
262 | if (start_pc >= ptr->start_pc | |
263 | && start_pc <= ptr->end_pc | |
264 | && TREE_PURPOSE (ptr->handlers) == type | |
265 | && TREE_VALUE (ptr->handlers) == handler) | |
266 | { | |
267 | /* Already found an overlapping range, so coalesce. */ | |
268 | ptr->end_pc = MAX (ptr->end_pc, end_pc); | |
269 | return; | |
270 | } | |
271 | prev = ptr; | |
272 | } | |
273 | ||
274 | h = (struct eh_range *) oballoc (sizeof (struct eh_range)); | |
275 | h->start_pc = start_pc; | |
276 | h->end_pc = end_pc; | |
277 | h->first_child = NULL; | |
278 | h->outer = NULL; | |
279 | h->handlers = build_tree_list (type, handler); | |
280 | h->next_sibling = NULL; | |
281 | ||
282 | if (prev == NULL) | |
283 | whole_range.first_child = h; | |
284 | else | |
285 | prev->next_sibling = h; | |
e04a16fb AG |
286 | } |
287 | ||
288 | ||
289 | /* if there are any handlers for this range, issue start of region */ | |
4bcde32e | 290 | static void |
e04a16fb | 291 | expand_start_java_handler (range) |
d4476be2 | 292 | struct eh_range *range ATTRIBUTE_UNUSED; |
e04a16fb | 293 | { |
6f9c8716 | 294 | push_obstacks (&permanent_obstack, &permanent_obstack); |
e04a16fb | 295 | expand_eh_region_start (); |
6f9c8716 | 296 | pop_obstacks (); |
e04a16fb AG |
297 | } |
298 | ||
e4de5a10 PB |
299 | tree |
300 | prepare_eh_table_type (type) | |
301 | tree type; | |
302 | { | |
303 | tree exp; | |
304 | ||
305 | /* The "type" (metch_info) in a (Java) exception table is one: | |
306 | * a) NULL - meaning match any type in a try-finally. | |
307 | * b) a pointer to a (ccmpiled) class (low-order bit 0). | |
308 | * c) a pointer to the Utf8Const name of the class, plus one | |
309 | * (which yields a value with low-order bit 1). */ | |
310 | ||
311 | push_obstacks (&permanent_obstack, &permanent_obstack); | |
312 | if (type == NULL_TREE) | |
313 | exp = null_pointer_node; | |
314 | else if (is_compiled_class (type)) | |
315 | exp = build_class_ref (type); | |
316 | else | |
317 | exp = fold (build | |
318 | (PLUS_EXPR, ptr_type_node, | |
319 | build_utf8_ref (build_internal_class_name (type)), | |
320 | size_one_node)); | |
321 | pop_obstacks (); | |
322 | return exp; | |
323 | } | |
324 | ||
e04a16fb AG |
325 | /* if there are any handlers for this range, isssue end of range, |
326 | and then all handler blocks */ | |
4bcde32e | 327 | static void |
e04a16fb AG |
328 | expand_end_java_handler (range) |
329 | struct eh_range *range; | |
330 | { | |
331 | tree handler = range->handlers; | |
6f9c8716 | 332 | push_obstacks (&permanent_obstack, &permanent_obstack); |
e04a16fb | 333 | expand_start_all_catch (); |
6f9c8716 | 334 | pop_obstacks (); |
e04a16fb AG |
335 | for ( ; handler != NULL_TREE; handler = TREE_CHAIN (handler)) |
336 | { | |
e4de5a10 PB |
337 | start_catch_handler (prepare_eh_table_type (TREE_PURPOSE (handler))); |
338 | /* Push the thrown object on the top of the stack */ | |
e04a16fb | 339 | expand_goto (TREE_VALUE (handler)); |
0974e9fe APB |
340 | expand_resume_after_catch (); |
341 | end_catch_handler (); | |
e04a16fb AG |
342 | } |
343 | expand_end_all_catch (); | |
344 | } | |
345 | ||
346 | /* Recursive helper routine for maybe_start_handlers. */ | |
347 | ||
348 | static void | |
349 | check_start_handlers (range, pc) | |
350 | struct eh_range *range; | |
351 | int pc; | |
352 | { | |
353 | if (range != NULL_EH_RANGE && range->start_pc == pc) | |
354 | { | |
355 | check_start_handlers (range->outer, pc); | |
356 | expand_start_java_handler (range); | |
357 | } | |
358 | } | |
359 | ||
360 | struct eh_range *current_range; | |
361 | ||
362 | /* Emit any start-of-try-range start at PC. */ | |
363 | ||
364 | void | |
365 | maybe_start_try (pc) | |
366 | int pc; | |
367 | { | |
368 | if (! doing_eh (1)) | |
369 | return; | |
370 | ||
371 | current_range = find_handler (pc); | |
372 | check_start_handlers (current_range, pc); | |
373 | } | |
374 | ||
375 | /* Emit any end-of-try-range end at PC. */ | |
376 | ||
377 | void | |
378 | maybe_end_try (pc) | |
379 | int pc; | |
380 | { | |
381 | if (! doing_eh (1)) | |
382 | return; | |
383 | ||
384 | while (current_range != NULL_EH_RANGE && current_range->end_pc <= pc) | |
385 | { | |
386 | expand_end_java_handler (current_range); | |
387 | current_range = current_range->outer; | |
388 | } | |
389 | } | |
390 | ||
391 | /* Emit the handler labels and their code */ | |
392 | ||
393 | void | |
394 | emit_handlers () | |
395 | { | |
396 | if (catch_clauses) | |
397 | { | |
398 | rtx funcend = gen_label_rtx (); | |
399 | emit_jump (funcend); | |
400 | ||
401 | emit_insns (catch_clauses); | |
9a260e99 | 402 | catch_clauses = NULL_RTX; |
e04a16fb AG |
403 | expand_leftover_cleanups (); |
404 | ||
405 | emit_label (funcend); | |
406 | } | |
407 | } | |
1b18747f AH |
408 | |
409 | /* Resume executing at the statement immediately after the end of an | |
410 | exception region. */ | |
411 | ||
412 | void | |
413 | expand_resume_after_catch () | |
414 | { | |
415 | expand_goto (top_label_entry (&caught_return_label_stack)); | |
416 | } |