diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /gcc/java/except.c | |
download | cbb-gcc-4.6.4-15d2061ac0796199866debe9ac87130894b0cdd3.tar.bz2 cbb-gcc-4.6.4-15d2061ac0796199866debe9ac87130894b0cdd3.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'gcc/java/except.c')
-rw-r--r-- | gcc/java/except.c | 611 |
1 files changed, 611 insertions, 0 deletions
diff --git a/gcc/java/except.c b/gcc/java/except.c new file mode 100644 index 000000000..1705106c4 --- /dev/null +++ b/gcc/java/except.c @@ -0,0 +1,611 @@ +/* Handle exceptions for GNU compiler for the Java(TM) language. + Copyright (C) 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005, + 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. + +Java and all Java-based marks are trademarks or registered trademarks +of Sun Microsystems, Inc. in the United States and other countries. +The Free Software Foundation is independent of Sun Microsystems, Inc. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "java-tree.h" +#include "javaop.h" +#include "java-opcodes.h" +#include "jcf.h" +#include "java-except.h" +#include "diagnostic-core.h" +#include "toplev.h" +#include "tree-iterator.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 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; + +/* These variables are used to speed up find_handler. */ + +static int cache_range_start, cache_range_end; +static struct eh_range *cache_range; +static struct eh_range *cache_next_child; + +/* A dummy range that represents the entire method. */ + +struct eh_range whole_range; + +/* Check the invariants of the structure we're using to contain + exception regions. Either returns true or fails an assertion + check. */ + +bool +sanity_check_exception_range (struct eh_range *range) +{ + struct eh_range *ptr = range->first_child; + for (; ptr; ptr = ptr->next_sibling) + { + gcc_assert (ptr->outer == range + && ptr->end_pc > ptr->start_pc); + if (ptr->next_sibling) + gcc_assert (ptr->next_sibling->start_pc >= ptr->end_pc); + gcc_assert (ptr->start_pc >= ptr->outer->start_pc + && ptr->end_pc <= ptr->outer->end_pc); + (void) sanity_check_exception_range (ptr); + } + return true; +} + +#if defined(DEBUG_JAVA_BINDING_LEVELS) +extern int is_class_level; +extern int current_pc; +extern int binding_depth; +extern void indent (void); +static void +print_ranges (struct eh_range *range) +{ + if (! range) + return; + + struct eh_range *child = range->first_child; + + indent (); + fprintf (stderr, "handler pc %d --> %d ", range->start_pc, range->end_pc); + + tree handler = range->handlers; + for ( ; handler != NULL_TREE; handler = TREE_CHAIN (handler)) + { + tree type = TREE_PURPOSE (handler); + if (type == NULL) + type = throwable_type_node; + fprintf (stderr, " type=%s ", IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)))); + } + fprintf (stderr, "\n"); + + int saved = binding_depth; + binding_depth++; + print_ranges (child); + binding_depth = saved; + + print_ranges (range->next_sibling); +} +#endif + +/* Search for the most specific eh_range containing PC. + Assume PC is within RANGE. + CHILD is a list of children of RANGE such that any + previous children have end_pc values that are too low. */ + +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; +} + +/* Find the inner-most handler that contains PC. */ + +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 +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; + } +} + +/* Called to re-initialize the exception machinery for a new method. */ + +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; +} + +/* Split an exception range into two at PC. The sub-ranges that + belong to the range are split and distributed between the two new + ranges. */ + +static void +split_range (struct eh_range *range, int pc) +{ + struct eh_range *ptr; + struct eh_range **first_child, **second_child; + struct eh_range *h; + + /* First, split all the sub-ranges. */ + for (ptr = range->first_child; ptr; ptr = ptr->next_sibling) + { + if (pc > ptr->start_pc + && pc < ptr->end_pc) + { + split_range (ptr, pc); + } + } + + /* Create a new range. */ + h = XNEW (struct eh_range); + + h->start_pc = pc; + h->end_pc = range->end_pc; + h->next_sibling = range->next_sibling; + range->next_sibling = h; + range->end_pc = pc; + h->handlers = build_tree_list (TREE_PURPOSE (range->handlers), + TREE_VALUE (range->handlers)); + h->next_sibling = NULL; + h->expanded = 0; + h->stmt = NULL; + h->outer = range->outer; + h->first_child = NULL; + + ptr = range->first_child; + first_child = &range->first_child; + second_child = &h->first_child; + + /* Distribute the sub-ranges between the two new ranges. */ + for (ptr = range->first_child; ptr; ptr = ptr->next_sibling) + { + if (ptr->start_pc < pc) + { + *first_child = ptr; + ptr->outer = range; + first_child = &ptr->next_sibling; + } + else + { + *second_child = ptr; + ptr->outer = h; + second_child = &ptr->next_sibling; + } + } + *first_child = NULL; + *second_child = NULL; +} + + +/* Add an exception range. + + There are some missed optimization opportunities here. For + example, some bytecode obfuscators generate seemingly + nonoverlapping exception ranges which, when coalesced, do in fact + nest correctly. We could merge these, but we'd have to fix up all + the enclosed regions first and perhaps create a new range anyway if + it overlapped existing ranges. + + Also, we don't attempt to detect the case where two previously + added disjoint ranges could be coalesced by a new range. */ + +void +add_handler (int start_pc, int end_pc, tree handler, tree type) +{ + struct eh_range *ptr, *h; + struct eh_range **first_child, **prev; + + /* First, split all the existing ranges that we need to enclose. */ + for (ptr = whole_range.first_child; ptr; ptr = ptr->next_sibling) + { + if (start_pc > ptr->start_pc + && start_pc < ptr->end_pc) + { + split_range (ptr, start_pc); + } + + if (end_pc > ptr->start_pc + && end_pc < ptr->end_pc) + { + split_range (ptr, end_pc); + } + + if (ptr->start_pc >= end_pc) + break; + } + + /* Create the new range. */ + h = XNEW (struct eh_range); + first_child = &h->first_child; + + h->start_pc = start_pc; + h->end_pc = end_pc; + h->first_child = NULL; + h->outer = NULL_EH_RANGE; + h->handlers = build_tree_list (type, handler); + h->next_sibling = NULL; + h->expanded = 0; + h->stmt = NULL; + + /* Find every range at the top level that will be a sub-range of the + range we're inserting and make it so. */ + { + struct eh_range **prev = &whole_range.first_child; + for (ptr = *prev; ptr;) + { + struct eh_range *next = ptr->next_sibling; + + if (ptr->start_pc >= end_pc) + break; + + if (ptr->start_pc < start_pc) + { + prev = &ptr->next_sibling; + } + else if (ptr->start_pc >= start_pc + && ptr->start_pc < end_pc) + { + *prev = next; + *first_child = ptr; + first_child = &ptr->next_sibling; + ptr->outer = h; + ptr->next_sibling = NULL; + } + + ptr = next; + } + } + + /* Find the right place to insert the new range. */ + prev = &whole_range.first_child; + for (ptr = *prev; ptr; prev = &ptr->next_sibling, ptr = ptr->next_sibling) + { + gcc_assert (ptr->outer == NULL_EH_RANGE); + if (ptr->start_pc >= start_pc) + break; + } + + /* And insert it there. */ + *prev = h; + if (ptr) + { + h->next_sibling = ptr; + h->outer = ptr->outer; + } +} + + +/* if there are any handlers for this range, issue start of region */ +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 /* defined(DEBUG_JAVA_BINDING_LEVELS) */ + 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; + + /* The "type" (match_info) in a (Java) exception table is a pointer to: + * a) NULL - meaning match any type in a try-finally. + * b) a pointer to a pointer to a class. + * c) a pointer to a pointer to a utf8_ref. The pointer is + * rewritten to point to the appropriate class. */ + + 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); + + 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 = (char *) alloca (strlen (name) + 5); + sprintf (buf, "%s_ref", name); + decl = build_decl (input_location, + 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 = (char *) alloca (strlen (name) + 5); + sprintf (buf, "%s_ref", name); + decl = build_decl (input_location, + 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); + CONSTRUCTOR_APPEND_ELT (TYPE_CATCH_CLASSES (output_class), + NULL_TREE, + make_catch_class_record (exp, utf8_ref)); + } + + 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; +} + +/* For every class in the TYPE_TO_RUNTIME_MAP, expand the + corresponding object that is used by the runtime type matcher. */ + +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); +} + +/* Build and push the variable that will hold the exception object + within this function. */ + +static tree +build_exception_object_var (void) +{ + tree decl = DECL_FUNCTION_EXC_OBJ (current_function_decl); + if (decl == NULL) + { + decl = build_decl (DECL_SOURCE_LOCATION (current_function_decl), + VAR_DECL, get_identifier ("#exc_obj"), ptr_type_node); + DECL_IGNORED_P (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + + DECL_FUNCTION_EXC_OBJ (current_function_decl) = decl; + pushdecl_function_level (decl); + } + return decl; +} + +/* Build a reference to the jthrowable object being carried in the + exception header. */ + +tree +build_exception_object_ref (tree type) +{ + tree obj; + + /* Java only passes object via pointer and doesn't require adjusting. + The java object is immediately before the generic exception header. */ + obj = build_exception_object_var (); + obj = fold_convert (build_pointer_type (type), obj); + obj = build2 (POINTER_PLUS_EXPR, TREE_TYPE (obj), obj, + fold_build1 (NEGATE_EXPR, sizetype, + TYPE_SIZE_UNIT (TREE_TYPE (obj)))); + obj = build1 (INDIRECT_REF, type, obj); + + return obj; +} + +/* If there are any handlers for this range, issue end of range, + and then all handler blocks */ +void +expand_end_java_handler (struct eh_range *range) +{ + tree handler = range->handlers; + if (handler) + { + tree exc_obj = build_exception_object_var (); + tree catches = make_node (STATEMENT_LIST); + tree_stmt_iterator catches_i = tsi_last (catches); + tree *body; + + for (; handler; handler = TREE_CHAIN (handler)) + { + tree type, eh_type, x; + tree stmts = make_node (STATEMENT_LIST); + tree_stmt_iterator stmts_i = tsi_last (stmts); + + type = TREE_PURPOSE (handler); + if (type == NULL) + type = throwable_type_node; + eh_type = prepare_eh_table_type (type); + + x = build_call_expr (built_in_decls[BUILT_IN_EH_POINTER], + 1, integer_zero_node); + x = build2 (MODIFY_EXPR, void_type_node, exc_obj, x); + tsi_link_after (&stmts_i, x, TSI_CONTINUE_LINKING); + + x = build1 (GOTO_EXPR, void_type_node, TREE_VALUE (handler)); + tsi_link_after (&stmts_i, x, TSI_CONTINUE_LINKING); + + x = build2 (CATCH_EXPR, void_type_node, eh_type, stmts); + tsi_link_after (&catches_i, x, TSI_CONTINUE_LINKING); + + /* Throwable can match anything in Java, and therefore + any subsequent handlers are unreachable. */ + /* ??? If we're assured of no foreign language exceptions, + we'd be better off using NULL as the exception type + for the catch. */ + if (type == throwable_type_node) + break; + } + + body = get_stmts (); + *body = build2 (TRY_CATCH_EXPR, void_type_node, *body, catches); + } + +#if defined(DEBUG_JAVA_BINDING_LEVELS) + indent (); + fprintf (stderr, "expand end handler pc %d <-- %d\n", + current_pc, range->start_pc); +#endif /* defined(DEBUG_JAVA_BINDING_LEVELS) */ +} + +/* Recursive helper routine for maybe_start_handlers. */ + +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); + } +} + + +/* Routine to see if exception handling is turned on. + DO_WARN is nonzero if we want to inform the user that exception + handling is turned off. + + This is used to ensure that -fexceptions has been specified if the + compiler tries to use any exception-specific functions. */ + +static inline int +doing_eh (void) +{ + if (! flag_exceptions) + { + static int warned = 0; + if (! warned) + { + error ("exception handling disabled, use -fexceptions to enable"); + warned = 1; + } + return 0; + } + return 1; +} + +static struct eh_range *current_range; + +/* Emit any start-of-try-range starting at start_pc and ending after + end_pc. */ + +void +maybe_start_try (int start_pc, int end_pc) +{ + struct eh_range *range; + if (! doing_eh ()) + 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); +} + |