#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "real.h"
#include "rtl.h"
#include "java-tree.h"
#include "javaop.h"
#include "java-opcodes.h"
#include "jcf.h"
#include "function.h"
#include "except.h"
#include "java-except.h"
#include "toplev.h"
static void expand_start_java_handler (struct eh_range *);
static struct eh_range *find_handler_in_range (int, struct eh_range *,
struct eh_range *);
static void link_handler (struct eh_range *, struct eh_range *);
static void check_start_handlers (struct eh_range *, int);
static void free_eh_ranges (struct eh_range *range);
struct eh_range *current_method_handlers;
struct eh_range *current_try_block = NULL;
struct eh_range *eh_range_freelist = NULL;
static int cache_range_start, cache_range_end;
static struct eh_range *cache_range;
static struct eh_range *cache_next_child;
struct eh_range whole_range;
#if defined(DEBUG_JAVA_BINDING_LEVELS)
extern int binding_depth;
extern int is_class_level;
extern int current_pc;
extern void indent ();
#endif
static struct eh_range *
find_handler_in_range (int pc, struct eh_range *range, struct eh_range *child)
{
for (; child != NULL; child = child->next_sibling)
{
if (pc < child->start_pc)
break;
if (pc < child->end_pc)
return find_handler_in_range (pc, child, child->first_child);
}
cache_range = range;
cache_range_start = pc;
cache_next_child = child;
cache_range_end = child == NULL ? range->end_pc : child->start_pc;
return range;
}
struct eh_range *
find_handler (int pc)
{
struct eh_range *h;
if (pc >= cache_range_start)
{
h = cache_range;
if (pc < cache_range_end)
return h;
while (pc >= h->end_pc)
{
cache_next_child = h->next_sibling;
h = h->outer;
}
}
else
{
h = &whole_range;
cache_next_child = h->first_child;
}
return find_handler_in_range (pc, h, cache_next_child);
}
static void
link_handler (struct eh_range *range, struct eh_range *outer)
{
struct eh_range **ptr;
if (range->start_pc == outer->start_pc && range->end_pc == outer->end_pc)
{
outer->handlers = chainon (outer->handlers, range->handlers);
return;
}
if (range->start_pc <= outer->start_pc && range->end_pc >= outer->end_pc)
{
range->outer = outer->outer;
range->next_sibling = NULL;
range->first_child = outer;
{
struct eh_range *p = outer;
struct eh_range **pr = &(outer->outer->first_child);
while (*pr != outer)
pr = &(*pr)->next_sibling;
*pr = range;
while (p)
{
p->outer = range;
p = p->next_sibling;
}
}
return;
}
if (range->start_pc < outer->start_pc || range->end_pc > outer->end_pc)
{
struct eh_range *h = xmalloc (sizeof (struct eh_range));
if (range->start_pc < outer->start_pc)
{
h->start_pc = range->start_pc;
h->end_pc = outer->start_pc;
range->start_pc = outer->start_pc;
}
else
{
h->start_pc = outer->end_pc;
h->end_pc = range->end_pc;
range->end_pc = outer->end_pc;
}
h->first_child = NULL;
h->outer = NULL;
h->handlers = build_tree_list (TREE_PURPOSE (range->handlers),
TREE_VALUE (range->handlers));
h->next_sibling = NULL;
h->expanded = 0;
h->stmt = NULL;
link_handler (h, &whole_range);
link_handler (range, &whole_range);
return;
}
ptr = &outer->first_child;
for (;; ptr = &(*ptr)->next_sibling)
{
if (*ptr == NULL || range->end_pc <= (*ptr)->start_pc)
{
range->next_sibling = *ptr;
range->first_child = NULL;
range->outer = outer;
*ptr = range;
return;
}
else if (range->start_pc < (*ptr)->end_pc)
{
link_handler (range, *ptr);
return;
}
}
}
void
handle_nested_ranges (void)
{
struct eh_range *ptr, *next;
ptr = whole_range.first_child;
whole_range.first_child = NULL;
for (; ptr; ptr = next)
{
next = ptr->next_sibling;
ptr->next_sibling = NULL;
link_handler (ptr, &whole_range);
}
}
static void
free_eh_ranges (struct eh_range *range)
{
while (range)
{
struct eh_range *next = range->next_sibling;
free_eh_ranges (range->first_child);
if (range != &whole_range)
free (range);
range = next;
}
}
void
method_init_exceptions (void)
{
free_eh_ranges (&whole_range);
whole_range.start_pc = 0;
whole_range.end_pc = DECL_CODE_LENGTH (current_function_decl) + 1;
whole_range.outer = NULL;
whole_range.first_child = NULL;
whole_range.next_sibling = NULL;
cache_range_start = 0xFFFFFF;
}
void
add_handler (int start_pc, int end_pc, tree handler, tree type)
{
struct eh_range *ptr, *prev = NULL, *h;
for (ptr = whole_range.first_child; ptr; ptr = ptr->next_sibling)
{
if (start_pc >= ptr->start_pc
&& start_pc <= ptr->end_pc
&& TREE_PURPOSE (ptr->handlers) == type
&& TREE_VALUE (ptr->handlers) == handler)
{
ptr->end_pc = MAX (ptr->end_pc, end_pc);
return;
}
prev = ptr;
}
h = xmalloc (sizeof (struct eh_range));
h->start_pc = start_pc;
h->end_pc = end_pc;
h->first_child = NULL;
h->outer = NULL;
h->handlers = build_tree_list (type, handler);
h->next_sibling = NULL;
h->expanded = 0;
h->stmt = NULL;
if (prev == NULL)
whole_range.first_child = h;
else
prev->next_sibling = h;
}
static void
expand_start_java_handler (struct eh_range *range)
{
#if defined(DEBUG_JAVA_BINDING_LEVELS)
indent ();
fprintf (stderr, "expand start handler pc %d --> %d\n",
current_pc, range->end_pc);
#endif
pushlevel (0);
register_exception_range (range, range->start_pc, range->end_pc);
range->expanded = 1;
}
tree
prepare_eh_table_type (tree type)
{
tree exp;
tree *slot;
const char *name;
char *buf;
tree decl;
tree utf8_ref;
if (type == NULL_TREE)
return NULL_TREE;
if (TYPE_TO_RUNTIME_MAP (output_class) == NULL)
TYPE_TO_RUNTIME_MAP (output_class) = java_treetreehash_create (10, 1);
slot = java_treetreehash_new (TYPE_TO_RUNTIME_MAP (output_class), type);
if (*slot != NULL)
return TREE_VALUE (*slot);
if (is_compiled_class (type) && !flag_indirect_dispatch)
{
name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)));
buf = alloca (strlen (name) + 5);
sprintf (buf, "%s_ref", name);
decl = build_decl (VAR_DECL, get_identifier (buf), ptr_type_node);
TREE_STATIC (decl) = 1;
DECL_ARTIFICIAL (decl) = 1;
DECL_IGNORED_P (decl) = 1;
TREE_READONLY (decl) = 1;
TREE_THIS_VOLATILE (decl) = 0;
DECL_INITIAL (decl) = build_class_ref (type);
layout_decl (decl, 0);
pushdecl (decl);
exp = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (decl)), decl);
}
else
{
utf8_ref = build_utf8_ref (DECL_NAME (TYPE_NAME (type)));
name = IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND (utf8_ref, 0)));
buf = alloca (strlen (name) + 5);
sprintf (buf, "%s_ref", name);
decl = build_decl (VAR_DECL, get_identifier (buf), utf8const_ptr_type);
TREE_STATIC (decl) = 1;
DECL_ARTIFICIAL (decl) = 1;
DECL_IGNORED_P (decl) = 1;
TREE_READONLY (decl) = 1;
TREE_THIS_VOLATILE (decl) = 0;
layout_decl (decl, 0);
pushdecl (decl);
exp = build1 (ADDR_EXPR, build_pointer_type (utf8const_ptr_type), decl);
TYPE_CATCH_CLASSES (output_class) =
tree_cons (NULL, make_catch_class_record (exp, utf8_ref),
TYPE_CATCH_CLASSES (output_class));
}
exp = convert (ptr_type_node, exp);
*slot = tree_cons (type, exp, NULL_TREE);
return exp;
}
static int
expand_catch_class (void **entry, void *x ATTRIBUTE_UNUSED)
{
struct treetreehash_entry *ite = (struct treetreehash_entry *) *entry;
tree addr = TREE_VALUE ((tree)ite->value);
tree decl;
STRIP_NOPS (addr);
decl = TREE_OPERAND (addr, 0);
rest_of_decl_compilation (decl, global_bindings_p (), 0);
return true;
}
void
java_expand_catch_classes (tree this_class)
{
if (TYPE_TO_RUNTIME_MAP (this_class))
htab_traverse
(TYPE_TO_RUNTIME_MAP (this_class),
expand_catch_class, NULL);
}
tree
build_exception_object_ref (tree type)
{
tree obj;
obj = build0 (EXC_PTR_EXPR, build_pointer_type (type));
obj = build2 (MINUS_EXPR, TREE_TYPE (obj), obj,
TYPE_SIZE_UNIT (TREE_TYPE (obj)));
obj = build1 (INDIRECT_REF, type, obj);
return obj;
}
void
expand_end_java_handler (struct eh_range *range)
{
tree handler = range->handlers;
for ( ; handler != NULL_TREE; handler = TREE_CHAIN (handler))
{
tree type = TREE_PURPOSE (handler);
if (type == NULL)
type = throwable_type_node;
type = prepare_eh_table_type (type);
{
tree catch_expr = build2 (CATCH_EXPR, void_type_node, type,
build1 (GOTO_EXPR, void_type_node,
TREE_VALUE (handler)));
tree try_catch_expr = build2 (TRY_CATCH_EXPR, void_type_node,
*get_stmts (), catch_expr);
*get_stmts () = try_catch_expr;
}
}
#if defined(DEBUG_JAVA_BINDING_LEVELS)
indent ();
fprintf (stderr, "expand end handler pc %d <-- %d\n",
current_pc, range->start_pc);
#endif
}
static void
check_start_handlers (struct eh_range *range, int pc)
{
if (range != NULL_EH_RANGE && range->start_pc == pc)
{
check_start_handlers (range->outer, pc);
if (!range->expanded)
expand_start_java_handler (range);
}
}
static struct eh_range *current_range;
void
maybe_start_try (int start_pc, int end_pc)
{
struct eh_range *range;
if (! doing_eh (1))
return;
range = find_handler (start_pc);
while (range != NULL_EH_RANGE && range->start_pc == start_pc
&& range->end_pc < end_pc)
range = range->outer;
current_range = range;
check_start_handlers (range, start_pc);
}