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