#include "config.h"
#include "system.h"
#include "tree.h"
#include "rtl.h"
#include "expr.h"
#include "libfuncs.h"
#include "cp-tree.h"
#include "flags.h"
#include "obstack.h"
#include "output.h"
#include "except.h"
#include "toplev.h"
static void push_eh_cleanup PARAMS ((tree));
static tree prepare_eh_type PARAMS ((tree));
static tree build_eh_type_type PARAMS ((tree));
static tree do_begin_catch PARAMS ((void));
static int dtor_nothrow PARAMS ((tree));
static tree do_end_catch PARAMS ((tree));
static void push_eh_cleanup PARAMS ((tree));
static bool decl_is_java_type PARAMS ((tree decl, int err));
static void initialize_handler_parm PARAMS ((tree, tree));
static tree do_allocate_exception PARAMS ((tree));
static int complete_ptr_ref_or_void_ptr_p PARAMS ((tree, tree));
static bool is_admissible_throw_operand PARAMS ((tree));
static int can_convert_eh PARAMS ((tree, tree));
static void check_handlers_1 PARAMS ((tree, tree));
static tree cp_protect_cleanup_actions PARAMS ((void));
#include "decl.h"
#include "obstack.h"
void
init_exception_processing ()
{
tree tmp;
push_namespace (std_identifier);
tmp = build_function_type (void_type_node, void_list_node);
terminate_node = build_cp_library_fn_ptr ("terminate", tmp);
TREE_THIS_VOLATILE (terminate_node) = 1;
TREE_NOTHROW (terminate_node) = 1;
pop_namespace ();
tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node);
tmp = build_function_type (void_type_node, tmp);
call_unexpected_node
= push_throw_library_fn (get_identifier ("__cxa_call_unexpected"), tmp);
eh_personality_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS
? "__gxx_personality_sj0"
: "__gxx_personality_v0");
lang_eh_runtime_type = build_eh_type_type;
lang_protect_cleanup_actions = &cp_protect_cleanup_actions;
}
static tree
cp_protect_cleanup_actions ()
{
return build_call (terminate_node, NULL_TREE);
}
static tree
prepare_eh_type (type)
tree type;
{
if (type == NULL_TREE)
return type;
if (type == error_mark_node)
return error_mark_node;
if (TREE_CODE (type) == REFERENCE_TYPE)
type = TREE_TYPE (type);
type = TYPE_MAIN_VARIANT (type);
return type;
}
static tree
build_eh_type_type (type)
tree type;
{
tree exp;
if (type == NULL_TREE || type == error_mark_node)
return type;
if (decl_is_java_type (type, 0))
exp = build_java_class_ref (TREE_TYPE (type));
else
exp = get_tinfo_decl (type);
mark_used (exp);
exp = build1 (ADDR_EXPR, ptr_type_node, exp);
return exp;
}
tree
build_exc_ptr ()
{
return build (EXC_PTR_EXPR, ptr_type_node);
}
static tree
do_begin_catch ()
{
tree fn;
fn = get_identifier ("__cxa_begin_catch");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
tree tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node);
fn = push_library_fn (fn, build_function_type (ptr_type_node, tmp));
}
return build_function_call (fn, tree_cons (NULL_TREE, build_exc_ptr (),
NULL_TREE));
}
static int
dtor_nothrow (type)
tree type;
{
tree fn;
if (type == NULL_TREE)
return 0;
if (! TYPE_HAS_DESTRUCTOR (type))
return 1;
fn = lookup_member (type, dtor_identifier, 0, 0);
fn = TREE_VALUE (fn);
return TREE_NOTHROW (fn);
}
static tree
do_end_catch (type)
tree type;
{
tree fn, cleanup;
fn = get_identifier ("__cxa_end_catch");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
fn = push_void_library_fn (fn, void_list_node);
TREE_NOTHROW (fn) = 0;
}
cleanup = build_function_call (fn, NULL_TREE);
TREE_NOTHROW (cleanup) = dtor_nothrow (type);
return cleanup;
}
static void
push_eh_cleanup (type)
tree type;
{
finish_decl_cleanup (NULL_TREE, do_end_catch (type));
}
static bool
decl_is_java_type (decl, err)
tree decl;
int err;
{
bool r = (TREE_CODE (decl) == POINTER_TYPE
&& TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE
&& TYPE_FOR_JAVA (TREE_TYPE (decl)));
if (err)
{
if (TREE_CODE (decl) == REFERENCE_TYPE
&& TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE
&& TYPE_FOR_JAVA (TREE_TYPE (decl)))
{
error ("type `%T' is disallowed in Java `throw' or `catch'",
decl);
}
if (r)
{
tree jthrow_node
= IDENTIFIER_GLOBAL_VALUE (get_identifier ("jthrowable"));
if (jthrow_node == NULL_TREE)
fatal_error
("call to Java `catch' or `throw' with `jthrowable' undefined");
jthrow_node = TREE_TYPE (TREE_TYPE (jthrow_node));
if (! DERIVED_FROM_P (jthrow_node, TREE_TYPE (decl)))
{
error ("type `%T' is not derived from `java::lang::Throwable'",
TREE_TYPE (decl));
}
}
}
return r;
}
void
choose_personality_routine (lang)
enum languages lang;
{
static enum {
chose_none,
chose_cpp,
chose_java,
gave_error
} state;
switch (state)
{
case gave_error:
return;
case chose_cpp:
if (lang != lang_cplusplus)
goto give_error;
return;
case chose_java:
if (lang != lang_java)
goto give_error;
return;
case chose_none:
;
}
switch (lang)
{
case lang_cplusplus:
state = chose_cpp;
break;
case lang_java:
state = chose_java;
eh_personality_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS
? "__gcj_personality_sj0"
: "__gcj_personality_v0");
break;
default:
abort ();
}
return;
give_error:
error ("mixing C++ and Java catches in a single translation unit");
state = gave_error;
}
static void
initialize_handler_parm (decl, exp)
tree decl;
tree exp;
{
tree init;
tree init_type;
TREE_USED (decl) = 1;
init_type = TREE_TYPE (decl);
if (! TYPE_PTR_P (init_type)
&& TREE_CODE (init_type) != REFERENCE_TYPE)
init_type = build_reference_type (init_type);
choose_personality_routine (decl_is_java_type (init_type, 0)
? lang_java : lang_cplusplus);
if (TREE_CODE (init_type) == REFERENCE_TYPE
&& TYPE_PTR_P (TREE_TYPE (init_type)))
exp = build_unary_op (ADDR_EXPR, exp, 1);
exp = ocp_convert (init_type, exp, CONV_IMPLICIT|CONV_FORCE_TEMP, 0);
init = convert_from_reference (exp);
if (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl)))
{
init = ocp_convert (TREE_TYPE (decl), init,
CONV_IMPLICIT|CONV_FORCE_TEMP, 0);
init = build1 (MUST_NOT_THROW_EXPR, TREE_TYPE (init), init);
}
DECL_INITIAL (decl) = error_mark_node;
decl = pushdecl (decl);
start_decl_1 (decl);
cp_finish_decl (decl, init, NULL_TREE,
LOOKUP_ONLYCONVERTING|DIRECT_BIND);
}
tree
expand_start_catch_block (decl)
tree decl;
{
tree exp = NULL_TREE;
tree type;
bool is_java;
if (! doing_eh (1))
return NULL_TREE;
if (decl && !complete_ptr_ref_or_void_ptr_p (TREE_TYPE (decl), NULL_TREE))
decl = NULL_TREE;
if (decl)
type = prepare_eh_type (TREE_TYPE (decl));
else
type = NULL_TREE;
is_java = false;
if (decl)
{
tree init;
if (decl_is_java_type (type, 1))
{
init = build_exc_ptr ();
init = build1 (NOP_EXPR, build_pointer_type (type), init);
init = build (MINUS_EXPR, TREE_TYPE (init), init,
TYPE_SIZE_UNIT (TREE_TYPE (init)));
init = build_indirect_ref (init, NULL);
is_java = true;
}
else
{
init = do_begin_catch ();
}
exp = create_temporary_var (ptr_type_node);
DECL_REGISTER (exp) = 1;
cp_finish_decl (exp, init, NULL_TREE, LOOKUP_ONLYCONVERTING);
finish_expr_stmt (build_modify_expr (exp, INIT_EXPR, init));
}
else
finish_expr_stmt (do_begin_catch ());
if (! is_java)
push_eh_cleanup (type);
if (decl)
initialize_handler_parm (decl, exp);
return type;
}
void
expand_end_catch_block ()
{
if (! doing_eh (1))
return;
if (in_function_try_handler
&& (DECL_CONSTRUCTOR_P (current_function_decl)
|| DECL_DESTRUCTOR_P (current_function_decl)))
finish_expr_stmt (build_throw (NULL_TREE));
}
tree
begin_eh_spec_block ()
{
tree r = build_stmt (EH_SPEC_BLOCK, NULL_TREE, NULL_TREE);
add_stmt (r);
return r;
}
void
finish_eh_spec_block (raw_raises, eh_spec_block)
tree raw_raises;
tree eh_spec_block;
{
tree raises;
RECHAIN_STMTS (eh_spec_block, EH_SPEC_STMTS (eh_spec_block));
for (raises = NULL_TREE;
raw_raises && TREE_VALUE (raw_raises);
raw_raises = TREE_CHAIN (raw_raises))
raises = tree_cons (NULL_TREE, prepare_eh_type (TREE_VALUE (raw_raises)),
raises);
EH_SPEC_RAISES (eh_spec_block) = raises;
}
static tree
do_allocate_exception (type)
tree type;
{
tree fn;
fn = get_identifier ("__cxa_allocate_exception");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
tree tmp = tree_cons (NULL_TREE, c_size_type_node, void_list_node);
fn = push_library_fn (fn, build_function_type (ptr_type_node, tmp));
}
return build_function_call (fn, tree_cons (NULL_TREE, size_in_bytes (type),
NULL_TREE));
}
#if 0
static tree
do_free_exception (ptr)
tree ptr;
{
tree fn;
fn = get_identifier ("__cxa_free_exception");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
fn = push_void_library_fn (fn, tree_cons (NULL_TREE, ptr_type_node,
void_list_node));
}
return build_function_call (fn, tree_cons (NULL_TREE, ptr, NULL_TREE));
}
#endif
tree
build_throw (exp)
tree exp;
{
tree fn;
if (exp == error_mark_node)
return exp;
if (processing_template_decl)
return build_min (THROW_EXPR, void_type_node, exp);
if (exp == null_node)
warning ("throwing NULL, which has integral, not pointer type");
if (exp != NULL_TREE)
{
if (!is_admissible_throw_operand (exp))
return error_mark_node;
}
if (! doing_eh (1))
return error_mark_node;
if (exp && decl_is_java_type (TREE_TYPE (exp), 1))
{
tree fn = get_identifier ("_Jv_Throw");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
tree tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node);
tmp = build_function_type (ptr_type_node, tmp);
fn = push_throw_library_fn (fn, tmp);
}
exp = build_function_call (fn, tree_cons (NULL_TREE, exp, NULL_TREE));
}
else if (exp)
{
tree throw_type;
tree cleanup;
tree stmt_expr;
tree compound_stmt;
tree object, ptr;
tree tmp;
fn = get_identifier ("__cxa_throw");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
if (cleanup_type == NULL_TREE)
{
tmp = void_list_node;
tmp = tree_cons (NULL_TREE, ptr_type_node, tmp);
tmp = build_function_type (void_type_node, tmp);
cleanup_type = build_pointer_type (tmp);
}
tmp = void_list_node;
tmp = tree_cons (NULL_TREE, cleanup_type, tmp);
tmp = tree_cons (NULL_TREE, ptr_type_node, tmp);
tmp = tree_cons (NULL_TREE, ptr_type_node, tmp);
tmp = build_function_type (void_type_node, tmp);
fn = push_throw_library_fn (fn, tmp);
}
begin_init_stmts (&stmt_expr, &compound_stmt);
exp = decay_conversion (exp);
my_friendly_assert (stmts_are_full_exprs_p () == 1, 19990926);
if (TREE_SIDE_EFFECTS (exp))
{
tmp = create_temporary_var (TREE_TYPE (exp));
DECL_INITIAL (tmp) = exp;
cp_finish_decl (tmp, exp, NULL_TREE, LOOKUP_ONLYCONVERTING);
exp = tmp;
}
ptr = create_temporary_var (ptr_type_node);
DECL_REGISTER (ptr) = 1;
cp_finish_decl (ptr, NULL_TREE, NULL_TREE, LOOKUP_ONLYCONVERTING);
tmp = do_allocate_exception (TREE_TYPE (exp));
tmp = build_modify_expr (ptr, INIT_EXPR, tmp);
finish_expr_stmt (tmp);
object = build1 (NOP_EXPR, build_pointer_type (TREE_TYPE (exp)), ptr);
object = build_indirect_ref (object, NULL);
exp = build_modify_expr (object, INIT_EXPR, exp);
if (exp == error_mark_node)
error (" in thrown expression");
exp = build1 (MUST_NOT_THROW_EXPR, TREE_TYPE (exp), exp);
finish_expr_stmt (exp);
throw_type = build_eh_type_type (prepare_eh_type (TREE_TYPE (object)));
if (TYPE_HAS_DESTRUCTOR (TREE_TYPE (object)))
{
cleanup = lookup_fnfields (TYPE_BINFO (TREE_TYPE (object)),
complete_dtor_identifier, 0);
cleanup = TREE_VALUE (cleanup);
mark_used (cleanup);
mark_addressable (cleanup);
cleanup = build1 (ADDR_EXPR, cleanup_type, cleanup);
}
else
{
cleanup = build_int_2 (0, 0);
TREE_TYPE (cleanup) = cleanup_type;
}
tmp = tree_cons (NULL_TREE, cleanup, NULL_TREE);
tmp = tree_cons (NULL_TREE, throw_type, tmp);
tmp = tree_cons (NULL_TREE, ptr, tmp);
tmp = build_function_call (fn, tmp);
finish_expr_stmt (tmp);
exp = finish_init_stmts (stmt_expr, compound_stmt);
}
else
{
tree fn = get_identifier ("__cxa_rethrow");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
fn = push_throw_library_fn
(fn, build_function_type (void_type_node, void_list_node));
}
exp = build_function_call (fn, NULL_TREE);
}
exp = build1 (THROW_EXPR, void_type_node, exp);
return exp;
}
static int
complete_ptr_ref_or_void_ptr_p (type, from)
tree type;
tree from;
{
int is_ptr;
type = complete_type_or_else (type, from);
if (!type)
return 0;
is_ptr = TREE_CODE (type) == POINTER_TYPE;
if (is_ptr || TREE_CODE (type) == REFERENCE_TYPE)
{
tree core = TREE_TYPE (type);
if (is_ptr && VOID_TYPE_P (core))
;
else if (!complete_type_or_else (core, from))
return 0;
}
return 1;
}
static bool
is_admissible_throw_operand (expr)
tree expr;
{
tree type = TREE_TYPE (expr);
if (!complete_ptr_ref_or_void_ptr_p (type, expr))
return false;
else if (CLASS_TYPE_P (type) && CLASSTYPE_PURE_VIRTUALS (type))
{
error ("expression '%E' of abstract class type '%T' cannot be used in throw-expression", expr, type);
return false;
}
return true;
}
#include "cfns.h"
int
nothrow_libfn_p (fn)
tree fn;
{
tree id;
if (TREE_PUBLIC (fn)
&& DECL_EXTERNAL (fn)
&& DECL_NAMESPACE_SCOPE_P (fn)
&& DECL_EXTERN_C_P (fn))
;
else
return 0;
id = DECL_ASSEMBLER_NAME (fn);
return !!libc_name_p (IDENTIFIER_POINTER (id), IDENTIFIER_LENGTH (id));
}
static int
can_convert_eh (to, from)
tree to, from;
{
if (TREE_CODE (to) == REFERENCE_TYPE)
to = TREE_TYPE (to);
if (TREE_CODE (from) == REFERENCE_TYPE)
from = TREE_TYPE (from);
if (TREE_CODE (to) == POINTER_TYPE && TREE_CODE (from) == POINTER_TYPE)
{
to = TREE_TYPE (to);
from = TREE_TYPE (from);
if (! at_least_as_qualified_p (to, from))
return 0;
if (TREE_CODE (to) == VOID_TYPE)
return 1;
}
if (CLASS_TYPE_P (to) && CLASS_TYPE_P (from)
&& PUBLICLY_UNIQUELY_DERIVED_P (to, from))
return 1;
return 0;
}
static void
check_handlers_1 (master, handlers)
tree master;
tree handlers;
{
tree type = TREE_TYPE (master);
tree handler;
for (handler = handlers; handler; handler = TREE_CHAIN (handler))
if (TREE_TYPE (handler)
&& can_convert_eh (type, TREE_TYPE (handler)))
{
lineno = STMT_LINENO (handler);
warning ("exception of type `%T' will be caught",
TREE_TYPE (handler));
lineno = STMT_LINENO (master);
warning (" by earlier handler for `%T'", type);
break;
}
}
void
check_handlers (handlers)
tree handlers;
{
tree handler;
int save_line = lineno;
for (handler = handlers; handler; handler = TREE_CHAIN (handler))
{
if (TREE_CHAIN (handler) == NULL_TREE)
;
else if (TREE_TYPE (handler) == NULL_TREE)
{
lineno = STMT_LINENO (handler);
pedwarn
("`...' handler must be the last handler for its try block");
}
else
check_handlers_1 (handler, TREE_CHAIN (handler));
}
lineno = save_line;
}
#ifdef PFE
void
pfe_init_exception_processing ()
{
eh_personality_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS
? "__gxx_personality_sj0"
: "__gxx_personality_v0");
lang_eh_runtime_type = build_eh_type_type;
lang_protect_cleanup_actions = &cp_protect_cleanup_actions;
}
#endif