1028 lines
30 KiB
C
1028 lines
30 KiB
C
/* Handle exceptional things in C++.
|
||
Copyright (C) 1989 Free Software Foundation, Inc.
|
||
Contributed by Michael Tiemann (tiemann@mcc.com)
|
||
|
||
This file is part of GNU CC.
|
||
|
||
GNU CC 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 1, or (at your option)
|
||
any later version.
|
||
|
||
GNU CC 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 GNU CC; see the file COPYING. If not, write to
|
||
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||
|
||
|
||
/* High-level class interface. */
|
||
|
||
#define NULL 0
|
||
#define EXCEPTION_NAME_PREFIX "__exception_"
|
||
|
||
#include "config.h"
|
||
#include "tree.h"
|
||
#include "cplus-tree.h"
|
||
#include "flags.h"
|
||
#include "assert.h"
|
||
/* On Suns this can get you to the right definition if you
|
||
set the right value for TARGET. */
|
||
#include <setjmp.h>
|
||
#ifdef sequent
|
||
/* Can you believe they forgot this? */
|
||
#define _JBLEN 11
|
||
#endif
|
||
|
||
#ifndef _JBLEN
|
||
#define _JBLEN (sizeof(jmp_buf)/sizeof(int))
|
||
#endif
|
||
|
||
void init_exception_processing ();
|
||
void init_exception_processing_1 ();
|
||
|
||
/* If non-zero, a VAR_DECL whose cleanup will cause a throw to the
|
||
next exception handler. Its value says whether to throw or not.
|
||
In the case of functions which do not issue a RAISE, it should be
|
||
possible to optimize away this VAR_DECL (and overhead associated
|
||
with it). */
|
||
tree exception_throw_decl;
|
||
/* Use this to know that we did not set `exception_throw_decl',
|
||
until GCC optimizer is smart enough to figure it out for itself. */
|
||
int sets_exception_throw_decl;
|
||
|
||
/* The exception `type' currently in scope, or NULL_TREE if none. */
|
||
tree current_exception_type;
|
||
|
||
/* The exception handler object for the given scope. */
|
||
tree current_exception_decl;
|
||
|
||
/* The ``object'' view of the current exception parameters.
|
||
We cast up from the `parms' field to `current_exception_type'. */
|
||
tree current_exception_object;
|
||
|
||
/* Low-level rtl interface. */
|
||
#include "rtl.h"
|
||
|
||
/* Cache `setjmp', `longjmp', `raise_exception', and `unhandled_exception'
|
||
after default conversion. Maybe later they will get built-in. */
|
||
static tree BISJ, BILJ, BIR, BIUE;
|
||
|
||
/* Local variables which give the appearance that exception
|
||
handling is part of the language and the execution model. */
|
||
|
||
/* The type of the exception handler stack. */
|
||
static tree EHS_type;
|
||
|
||
/* The global handler stack. */
|
||
tree EHS_decl;
|
||
|
||
/* Cached component refs to fields of `EHS_decl'. */
|
||
static tree EHS_prev, EHS_handler, EHS_parms, EHS_name;
|
||
|
||
/* The parameter names of this exception type. */
|
||
|
||
static tree last_exception_fields;
|
||
static tree last_exception_field_types;
|
||
|
||
/* When ID is VOID_TYPE_NODE, it means ``raise all''.
|
||
Cannot be inline, since it uses `alloca', and that
|
||
breaks code which pushes the result of this function
|
||
on the stack. */
|
||
static tree
|
||
exception_object_name (prefix, id)
|
||
tree prefix;
|
||
tree id;
|
||
{
|
||
/* First, cons up the `name' of this exception. */
|
||
char *name;
|
||
int length = (id == void_type_node ? 3 : IDENTIFIER_LENGTH (id)) + EXCEPTION_NAME_LENGTH;
|
||
|
||
if (prefix)
|
||
length += IDENTIFIER_LENGTH (prefix) + 2;
|
||
|
||
name = (char *)alloca (length);
|
||
strcpy (name, EXCEPTION_NAME_PREFIX);
|
||
length = EXCEPTION_NAME_LENGTH;
|
||
if (prefix)
|
||
{
|
||
strcpy (name + length, IDENTIFIER_POINTER (prefix));
|
||
name[length + IDENTIFIER_LENGTH (prefix)] = JOINER;
|
||
length += IDENTIFIER_LENGTH (prefix) + 1;
|
||
}
|
||
if (id == void_type_node)
|
||
strcpy (name + length, "all");
|
||
else
|
||
strcpy (name + length, IDENTIFIER_POINTER (id));
|
||
return get_identifier (name);
|
||
}
|
||
|
||
tree
|
||
lookup_exception_cname (ctype, cname, raise_id)
|
||
tree ctype, cname;
|
||
tree raise_id;
|
||
{
|
||
tree this_cname = TREE_PURPOSE (raise_id);
|
||
if (this_cname == NULL_TREE)
|
||
{
|
||
if (cname)
|
||
{
|
||
tree name = TREE_VALUE (raise_id);
|
||
if (purpose_member (name, CLASSTYPE_TAGS (ctype)))
|
||
this_cname = cname;
|
||
}
|
||
}
|
||
else if (this_cname == void_type_node)
|
||
this_cname = NULL_TREE;
|
||
else if (TREE_CODE (this_cname) != IDENTIFIER_NODE)
|
||
{
|
||
sorry ("multiple scope refs in `cplus_expand_raise_stmt'");
|
||
this_cname = error_mark_node;
|
||
}
|
||
return this_cname;
|
||
}
|
||
|
||
tree
|
||
lookup_exception_tname (oname)
|
||
tree oname;
|
||
{
|
||
return get_identifier (IDENTIFIER_POINTER (oname) + EXCEPTION_NAME_LENGTH);
|
||
}
|
||
|
||
tree
|
||
lookup_exception_object (cname, name, complain)
|
||
tree cname, name;
|
||
int complain;
|
||
{
|
||
tree oname;
|
||
tree decl;
|
||
|
||
if (cname == void_type_node)
|
||
cname = NULL_TREE;
|
||
else if (cname && TREE_CODE (cname) != IDENTIFIER_NODE)
|
||
{
|
||
sorry ("multiple scope refs in `lookup_exception_object'");
|
||
cname = NULL_TREE;
|
||
}
|
||
oname = exception_object_name (cname, name);
|
||
decl = IDENTIFIER_GLOBAL_VALUE (oname);
|
||
if (decl == NULL_TREE || TREE_CODE (decl) != VAR_DECL)
|
||
{
|
||
if (complain)
|
||
{
|
||
int temp = allocation_temporary_p ();
|
||
if (cname)
|
||
error ("no exception name object for name `%s::%s'",
|
||
IDENTIFIER_POINTER (cname),
|
||
IDENTIFIER_POINTER (name));
|
||
else
|
||
error ("no exception name object for name `%s'",
|
||
IDENTIFIER_POINTER (name));
|
||
if (temp)
|
||
end_temporary_allocation ();
|
||
/* Avoid further error messages. */
|
||
pushdecl_top_level (build_lang_field_decl (VAR_DECL,
|
||
exception_object_name (cname, name),
|
||
error_mark_node));
|
||
if (temp)
|
||
resume_temporary_allocation ();
|
||
}
|
||
return NULL_TREE;
|
||
}
|
||
return decl;
|
||
}
|
||
|
||
tree
|
||
lookup_exception_type (ctype, cname, raise_id)
|
||
tree ctype, cname;
|
||
tree raise_id;
|
||
{
|
||
tree name = TREE_VALUE (raise_id);
|
||
tree purpose = TREE_PURPOSE (raise_id);
|
||
|
||
if (cname && purpose == NULL_TREE)
|
||
purpose = cname;
|
||
|
||
if (purpose && purpose != void_type_node)
|
||
{
|
||
tree assoc = NULL_TREE;
|
||
|
||
if (TREE_CODE (purpose) != IDENTIFIER_NODE)
|
||
{
|
||
sorry ("multiple scope refs in `lookup_exception_type'");
|
||
TREE_PURPOSE (raise_id) = NULL_TREE;
|
||
return NULL_TREE;
|
||
}
|
||
if (! is_aggr_typedef (purpose, 1))
|
||
return NULL_TREE;
|
||
ctype = TREE_TYPE (TREE_TYPE (purpose));
|
||
assoc = purpose_member (name, CLASSTYPE_TAGS (ctype));
|
||
if (assoc)
|
||
return TREE_VALUE (assoc);
|
||
}
|
||
|
||
ctype = lookup_name (name);
|
||
if (ctype && TREE_CODE (ctype) == TYPE_DECL)
|
||
ctype = TREE_TYPE (ctype);
|
||
if (ctype && TREE_CODE (ctype) == RECORD_TYPE
|
||
&& CLASSTYPE_DECLARED_EXCEPTION (ctype))
|
||
return ctype;
|
||
return NULL_TREE;
|
||
}
|
||
|
||
tree
|
||
finish_exception (e, list_of_fieldlists)
|
||
tree e;
|
||
tree list_of_fieldlists;
|
||
{
|
||
tree parmtypes = NULL_TREE, name_field;
|
||
tree cname = TYPE_NAME (e);
|
||
|
||
if (TREE_CODE (cname) == TYPE_DECL)
|
||
cname = DECL_NAME (cname);
|
||
|
||
if (last_exception_fields)
|
||
error ("cannot declare exceptions within exceptions");
|
||
if (list_of_fieldlists && ! ANON_AGGRNAME_P (cname))
|
||
error_with_aggr_type (e, "exception name `%s' must follow body declaration");
|
||
if (list_of_fieldlists)
|
||
{
|
||
tree prev, field;
|
||
|
||
/* Note: no public, private, or protected allowed. */
|
||
if (TREE_CHAIN (list_of_fieldlists))
|
||
error ("visibility declarations invalid in exception declaration");
|
||
else if (TREE_PURPOSE (list_of_fieldlists) != (tree)visibility_default)
|
||
error ("visibility declarations invalid in exception declaration");
|
||
TREE_PURPOSE (list_of_fieldlists) = (tree)visibility_default;
|
||
|
||
/* Note also: no member function declarations allowed. */
|
||
for (prev = 0, field = TREE_VALUE (list_of_fieldlists);
|
||
field; prev = field, field = TREE_CHAIN (field))
|
||
{
|
||
switch (TREE_CODE (field))
|
||
{
|
||
case FIELD_DECL:
|
||
/* ok. */
|
||
parmtypes = tree_cons (NULL_TREE, TREE_TYPE (field), parmtypes);
|
||
continue;
|
||
case FUNCTION_DECL:
|
||
error_with_decl (field, "declaration of function `%s' in exception invalid");
|
||
break;
|
||
case VAR_DECL:
|
||
if (TREE_STATIC (field))
|
||
error_with_decl (field, "declaration of static variable `%s' in exception invalid");
|
||
else
|
||
error_with_decl (field, "declaration of constant field `%s' in exception invalid");
|
||
break;
|
||
case CONST_DECL:
|
||
error_with_decl (field, "declaration of enum value `%s' in exception invalid");
|
||
break;
|
||
case SCOPE_REF:
|
||
error ("use of `::' in exception context invalid");
|
||
break;
|
||
}
|
||
if (prev)
|
||
TREE_CHAIN (prev) = TREE_CHAIN (field);
|
||
else
|
||
TREE_VALUE (list_of_fieldlists) = TREE_CHAIN (field);
|
||
}
|
||
}
|
||
|
||
/* Now that we've cleaned up the fields, add a name identifier at front. */
|
||
name_field = build_lang_field_decl (FIELD_DECL, get_identifier ("__name"),
|
||
ptr_type_node);
|
||
if (list_of_fieldlists)
|
||
{
|
||
TREE_CHAIN (name_field) = TREE_VALUE (list_of_fieldlists);
|
||
TREE_VALUE (list_of_fieldlists) = name_field;
|
||
}
|
||
else
|
||
list_of_fieldlists = build_tree_list (NULL_TREE, name_field);
|
||
|
||
last_exception_fields = TREE_VALUE (list_of_fieldlists);
|
||
if (parmtypes)
|
||
{
|
||
last_exception_field_types = nreverse (parmtypes);
|
||
/* Set the TREE_CHAIN of what is now at the end of the
|
||
list to `void_list_node'. */
|
||
TREE_CHAIN (parmtypes) = void_list_node;
|
||
}
|
||
else
|
||
last_exception_field_types = void_list_node;
|
||
|
||
popclass (0);
|
||
|
||
#if 0
|
||
/* Remove aggregate types from the list of tags,
|
||
since these appear at global scope. */
|
||
while (x && IS_AGGR_TYPE (TREE_VALUE (x)))
|
||
x = TREE_CHAIN (x);
|
||
CLASSTYPE_TAGS (t) = x;
|
||
y = x;
|
||
while (x)
|
||
{
|
||
if (IS_AGGR_TYPE (TREE_VALUE (x)))
|
||
TREE_CHAIN (y) = TREE_CHAIN (x);
|
||
x = TREE_CHAIN (x);
|
||
}
|
||
#endif
|
||
|
||
if (flag_cadillac)
|
||
cadillac_finish_exception (e);
|
||
|
||
return e;
|
||
}
|
||
|
||
void
|
||
finish_exception_decl (cname, decl)
|
||
tree cname, decl;
|
||
{
|
||
/* In cplus-decl.h. */
|
||
extern tree last_function_parms;
|
||
|
||
/* An exception declaration. */
|
||
tree t, ctor;
|
||
tree parmdecls = NULL_TREE, fields;
|
||
tree list_of_fieldlists = temp_tree_cons (NULL_TREE,
|
||
copy_list (last_exception_fields),
|
||
NULL_TREE);
|
||
tree edecl = build_lang_field_decl (VAR_DECL,
|
||
exception_object_name (cname, DECL_NAME (decl)),
|
||
ptr_type_node);
|
||
|
||
DECL_LANGUAGE (edecl) = lang_c;
|
||
TREE_STATIC (edecl) = 1;
|
||
TREE_PUBLIC (edecl) = 1;
|
||
finish_decl (pushdecl (edecl), 0, 0);
|
||
|
||
/* Now instantiate the exception decl. */
|
||
t = xref_tag (exception_type_node, DECL_NAME (decl), NULL_TREE);
|
||
|
||
/* finish_struct will pop this. */
|
||
pushclass (t, 0);
|
||
|
||
/* Now add a constructor which takes as parameters all the types we
|
||
just defined. */
|
||
ctor = build_lang_decl (FUNCTION_DECL, DECL_NAME (decl),
|
||
build_cplus_method_type (t, TYPE_POINTER_TO (t),
|
||
last_exception_field_types));
|
||
/* Don't take `name'. The constructor handles that. */
|
||
fields = TREE_CHAIN (TREE_VALUE (list_of_fieldlists));
|
||
while (fields)
|
||
{
|
||
tree parm = build_decl (PARM_DECL, DECL_NAME (fields), TREE_TYPE (fields));
|
||
/* Since there is a prototype, args are passed in their own types. */
|
||
DECL_ARG_TYPE (parm) = TREE_TYPE (parm);
|
||
#ifdef PROMOTE_PROTOTYPES
|
||
if (TREE_CODE (TREE_TYPE (fields)) == INTEGER_TYPE
|
||
&& TYPE_PRECISION (TREE_TYPE (fields)) < TYPE_PRECISION (integer_type_node))
|
||
DECL_ARG_TYPE (parm) = integer_type_node;
|
||
#endif
|
||
TREE_CHAIN (parm) = parmdecls;
|
||
parmdecls = parm;
|
||
fields = TREE_CHAIN (fields);
|
||
}
|
||
fields = TREE_VALUE (list_of_fieldlists);
|
||
last_function_parms = nreverse (parmdecls);
|
||
|
||
DECL_CONSTRUCTOR_P (ctor) = 1;
|
||
TYPE_HAS_CONSTRUCTOR (t) = 1;
|
||
grokclassfn (t, DECL_NAME (decl), ctor, NO_SPECIAL, 0, NULL_TREE);
|
||
TREE_EXTERNAL (ctor) = 1;
|
||
TREE_STATIC (ctor) = 1;
|
||
TREE_PUBLIC (ctor) = 0;
|
||
TREE_INLINE (ctor) = 1;
|
||
make_decl_rtl (ctor, 0, 1);
|
||
finish_decl (ctor, NULL_TREE, 0);
|
||
TREE_CHAIN (ctor) = TREE_VALUE (list_of_fieldlists);
|
||
TREE_VALUE (list_of_fieldlists) = ctor;
|
||
|
||
finish_struct (t, list_of_fieldlists, 0, 0);
|
||
|
||
if (current_function_decl)
|
||
error ("cannot define exception inside function scope");
|
||
else
|
||
{
|
||
/* Now build the constructor for this exception. */
|
||
parmdecls = DECL_ARGUMENTS (ctor);
|
||
start_function (NULL_TREE, ctor, 0, 1);
|
||
store_parm_decls ();
|
||
pushlevel (0);
|
||
clear_last_expr ();
|
||
push_momentary ();
|
||
expand_start_bindings (0);
|
||
|
||
/* Move all the parameters to the fields, skipping `this'. */
|
||
parmdecls = TREE_CHAIN (parmdecls);
|
||
/* Install `name' of this exception handler. */
|
||
DECL_INITIAL (fields) = build_unary_op (ADDR_EXPR, edecl, 0);
|
||
fields = TREE_CHAIN (fields);
|
||
/* Install all the values. */
|
||
while (fields)
|
||
{
|
||
/* Set up the initialization for this field. */
|
||
DECL_INITIAL (fields) = parmdecls;
|
||
fields = TREE_CHAIN (fields);
|
||
parmdecls = TREE_CHAIN (parmdecls);
|
||
}
|
||
emit_base_init (t, 0);
|
||
|
||
finish_function (DECL_SOURCE_LINE (ctor), 1);
|
||
}
|
||
}
|
||
|
||
void
|
||
end_exception_decls ()
|
||
{
|
||
last_exception_field_types = NULL_TREE;
|
||
last_exception_fields = NULL_TREE;
|
||
}
|
||
|
||
/* Statement-level exception semantics. */
|
||
|
||
void
|
||
cplus_expand_start_try (implicit)
|
||
int implicit;
|
||
{
|
||
tree call_to_setjmp;
|
||
tree handler, ref;
|
||
|
||
/* Start a new block enclosing the whole handler. */
|
||
if (implicit)
|
||
{
|
||
pushlevel_temporary (1);
|
||
}
|
||
else
|
||
{
|
||
pushlevel (0);
|
||
clear_last_expr ();
|
||
push_momentary ();
|
||
|
||
/* Encompass whole exception handler in one big binding contour.
|
||
If RAISE should throw out of the whole TRY/EXCEPT block, call
|
||
`expand_start_bindings' with argument of 1. */
|
||
expand_start_bindings (0);
|
||
}
|
||
|
||
/* Allocate handler in that block. It's real name will come later.
|
||
Note that it will be the first name in this binding contour. */
|
||
handler = get_temp_name (EHS_type, 0);
|
||
DECL_INITIAL (handler) = error_mark_node;
|
||
finish_decl (handler, NULL_TREE, 0);
|
||
|
||
/* Must come after call to `finish_decl', else the cleanup for the temp
|
||
for the handler will cause the contour we just created to be popped. */
|
||
if (implicit)
|
||
declare_implicit_exception ();
|
||
|
||
/* Catch via `setjmp'. */
|
||
ref = build_component_ref (handler, get_identifier ("handler"), NULL_TREE, 0);
|
||
call_to_setjmp = build_function_call (BISJ, build_tree_list (NULL_TREE, ref));
|
||
|
||
/* RAISE throws to EXCEPT part. */
|
||
expand_start_try (build_binary_op (EQ_EXPR, call_to_setjmp, integer_zero_node), 0, 1);
|
||
}
|
||
|
||
/* If KEEP is 1, then declarations in the TRY statement are worth keeping.
|
||
If KEEP is 2, then the TRY statement was generated by the compiler.
|
||
If KEEP is 0, the declarations in the TRY statement contain errors. */
|
||
|
||
tree
|
||
cplus_expand_end_try (keep)
|
||
int keep;
|
||
{
|
||
tree decls, decl, block;
|
||
|
||
if (keep < 2)
|
||
pop_implicit_try_blocks (NULL_TREE);
|
||
|
||
decls = getdecls ();
|
||
|
||
/* Emit code to avoid falling through into a default
|
||
handler that might come later. */
|
||
expand_end_try ();
|
||
|
||
/* Pops binding contour local to TRY, and get the exception handler
|
||
object built by `...start_try'. */
|
||
switch (keep)
|
||
{
|
||
case 0:
|
||
expand_end_bindings (decls, 0, 1);
|
||
block = poplevel (0, 0, 0);
|
||
pop_momentary ();
|
||
decl = getdecls ();
|
||
break;
|
||
|
||
case 1:
|
||
expand_end_bindings (decls, 1, 1);
|
||
block = poplevel (1, 1, 0);
|
||
pop_momentary ();
|
||
decl = getdecls ();
|
||
break;
|
||
|
||
default:
|
||
decl = tree_last (decls);
|
||
block = NULL_TREE;
|
||
break;
|
||
}
|
||
|
||
assert (TREE_CODE (decl) == VAR_DECL && TREE_TYPE (decl) == EHS_type);
|
||
if (block)
|
||
{
|
||
TREE_LANG_FLAG_1 (block) = 1;
|
||
TREE_USED (block) = 1;
|
||
}
|
||
|
||
/* Pass it back so that its rtl can be bound to its name
|
||
(or vice versa). */
|
||
return decl;
|
||
}
|
||
|
||
void
|
||
cplus_expand_start_except (name, decl)
|
||
tree name, decl;
|
||
{
|
||
int yes;
|
||
tree tmp;
|
||
|
||
expand_start_except (0, 1);
|
||
|
||
/* This is internal `eh'. */
|
||
current_exception_decl = decl;
|
||
/* Get the exception object into scope (user declared `ex'). */
|
||
tmp = pushdecl (build_decl (VAR_DECL, name, ptr_type_node));
|
||
DECL_INITIAL (tmp) = error_mark_node;
|
||
finish_decl (tmp, build (COMPONENT_REF, ptr_type_node, decl, TREE_OPERAND (EHS_parms, 1)), 0);
|
||
current_exception_type = NULL_TREE;
|
||
yes = suspend_momentary ();
|
||
/* From now on, send the user to our faked-up object. */
|
||
current_exception_object = build1 (INDIRECT_REF, void_type_node, tmp);
|
||
IDENTIFIER_LOCAL_VALUE (name) = current_exception_object;
|
||
resume_momentary (yes);
|
||
|
||
/* Pop exception handler stack. */
|
||
expand_assignment (EHS_decl, EHS_prev, 0, 0);
|
||
}
|
||
|
||
/* Generate the call to `unhandled_exception' that is appropriate
|
||
for this particular unhandled exception. */
|
||
static tree
|
||
call_to_unhandled_exception ()
|
||
{
|
||
extern int lineno;
|
||
tree parms = tree_cons (NULL_TREE,
|
||
combine_strings (build_string (strlen (input_filename + 1), input_filename)),
|
||
build_tree_list (NULL_TREE, build_int_2 (lineno, 0)));
|
||
return build_function_call (BIUE, parms);
|
||
}
|
||
|
||
/* Note that this must be mirror image of `...start_try'.
|
||
DFAULT is the default clause, if there was one.
|
||
DFAULT is ERROR_MARK_NODE when this ends an implicit handler. */
|
||
void
|
||
cplus_expand_end_except (dfault)
|
||
tree dfault;
|
||
{
|
||
extern tree expand_end_except (); /* stmt.c. */
|
||
tree decls, raised;
|
||
|
||
if (dfault == NULL_TREE)
|
||
{
|
||
/* Uncaught exception at outermost level. If raised locally,
|
||
reraise the exception. Otherwise, generate code to call `abort'. */
|
||
if (in_try_block (1) == 0)
|
||
{
|
||
expand_start_cond (build (EQ_EXPR, integer_type_node,
|
||
exception_throw_decl, integer_zero_node), 0);
|
||
expand_expr (call_to_unhandled_exception (), 0, VOIDmode, 0);
|
||
expand_end_cond ();
|
||
}
|
||
/* Try the next handler. */
|
||
if (! expand_escape_except ())
|
||
compiler_error ("except nesting botch");
|
||
}
|
||
|
||
raised = expand_end_except ();
|
||
|
||
decls = getdecls ();
|
||
expand_end_bindings (decls, decls != 0, 1);
|
||
poplevel (decls != 0, 1, 0);
|
||
|
||
/* Implicit handlers do not use the momentary obstack. */
|
||
if (dfault != error_mark_node)
|
||
pop_momentary ();
|
||
|
||
if (! in_try_block (1))
|
||
{
|
||
/* Check that this function is not raising exceptions
|
||
it is not supposed to. */
|
||
while (raised)
|
||
{
|
||
error_with_decl (TREE_VALUE (raised), "exception `%s' raised but not declared raisable");
|
||
raised = TREE_CHAIN (raised);
|
||
}
|
||
}
|
||
else if (dfault == NULL_TREE || dfault == error_mark_node)
|
||
{
|
||
expand_start_cond (build (NE_EXPR, integer_type_node,
|
||
exception_throw_decl,
|
||
integer_zero_node), 0);
|
||
/* We fell off the end of this try block. Try going to the next.
|
||
The escape_label will be the beginning of the next try block. */
|
||
if (! expand_escape_except ())
|
||
compiler_error ("except nesting botch");
|
||
expand_end_cond ();
|
||
}
|
||
}
|
||
|
||
/* Generate code to raise exception RAISE_ID.
|
||
If EXP is NULL_TREE, then PARMS is the list of parameters to use
|
||
for constructing this exception.
|
||
If EXP is non-NULL, then it is an already constructed object
|
||
of the kind that we want. */
|
||
void
|
||
cplus_expand_raise (raise_id, parms, exp)
|
||
tree raise_id;
|
||
tree parms;
|
||
tree exp;
|
||
{
|
||
/* Allocate new exception of appropriate type, passing
|
||
PARMS to its constructor. */
|
||
tree cname, name;
|
||
tree decl;
|
||
tree xexp = exp;
|
||
|
||
cname = lookup_exception_cname (current_class_type, current_class_name, raise_id);
|
||
if (cname == error_mark_node)
|
||
return;
|
||
name = TREE_VALUE (raise_id);
|
||
|
||
decl = lookup_exception_object (cname, name, 1);
|
||
if (decl == NULL_TREE)
|
||
return;
|
||
|
||
if (exp == NULL_TREE)
|
||
{
|
||
exp = build_method_call (NULL_TREE, name, parms, NULL_TREE, LOOKUP_COMPLAIN);
|
||
if (exp == error_mark_node)
|
||
return;
|
||
}
|
||
|
||
if (in_try_block (1))
|
||
{
|
||
expand_raise (decl);
|
||
}
|
||
else if (! current_function_decl)
|
||
{
|
||
if (xexp == NULL_TREE)
|
||
error_with_decl (decl, "invalid raise of `%s' outside of functions");
|
||
else
|
||
error_with_decl (decl, "invalid reraise of `%s' outside of functions");
|
||
}
|
||
else
|
||
{
|
||
/* Test this raise against what this function permits. */
|
||
tree names = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (current_function_decl));
|
||
while (names)
|
||
{
|
||
if (decl == TREE_TYPE (names))
|
||
break;
|
||
names = TREE_CHAIN (names);
|
||
}
|
||
if (names == NULL_TREE)
|
||
{
|
||
error ("current function not declared to raise exception `%s'",
|
||
IDENTIFIER_POINTER (name));
|
||
return;
|
||
}
|
||
}
|
||
|
||
expand_assignment (EHS_parms, exp, 0, 0);
|
||
|
||
/* Set the global exception handler stack's NAME field
|
||
to the `name' of this exception. The global exception
|
||
handler stack is the container for the exception object
|
||
we just built.
|
||
|
||
We go through a function call to make life easier when debugging. */
|
||
#if 0
|
||
expand_assignment (EHS_name, build_unary_op (ADDR_EXPR, decl, 0), 0, 0);
|
||
#else
|
||
parms = tree_cons (NULL_TREE, build_unary_op (ADDR_EXPR, EHS_name, 0),
|
||
build_tree_list (NULL_TREE,
|
||
build_unary_op (ADDR_EXPR, decl, 0)));
|
||
expand_expr (build_function_call (BIR, parms));
|
||
#endif
|
||
|
||
/* Activate thrower. If we are inside a TRY statement,
|
||
we can cheat and not do this, saving a longjmp. */
|
||
if (in_try_block (1) == 0)
|
||
{
|
||
sets_exception_throw_decl = 1;
|
||
expand_assignment (exception_throw_decl, integer_one_node, 0, 0);
|
||
}
|
||
|
||
if (xexp == NULL_TREE)
|
||
{
|
||
/* Invoke destructors for current procedure or handler. */
|
||
if (! expand_escape_except ())
|
||
compiler_error ("except nesting botch");
|
||
/* Throw via `longjmp'... Done as side-effect of goto. */
|
||
}
|
||
/* If we were re-raising, just let this fall through.
|
||
At the end of the reraises, the call to `expand_goto'
|
||
will take care of everybody. */
|
||
}
|
||
|
||
tree
|
||
cplus_expand_start_catch (raise_id)
|
||
tree raise_id;
|
||
{
|
||
tree cname = lookup_exception_cname (current_class_type, current_class_name, raise_id);
|
||
tree decl;
|
||
tree ref, cond;
|
||
|
||
if (cname == error_mark_node)
|
||
{
|
||
decl = error_mark_node;
|
||
cond = error_mark_node;
|
||
}
|
||
else
|
||
{
|
||
decl = lookup_exception_object (cname, TREE_VALUE (raise_id), 1);
|
||
if (decl == NULL_TREE)
|
||
{
|
||
cond = error_mark_node;
|
||
}
|
||
else
|
||
{
|
||
ref = build (COMPONENT_REF, ptr_type_node,
|
||
current_exception_decl, TREE_OPERAND (EHS_name, 1));
|
||
cond = build_binary_op (EQ_EXPR, build_unary_op (ADDR_EXPR, decl, 0), ref);
|
||
}
|
||
}
|
||
expand_start_cond (cond, 0);
|
||
|
||
/* Does nothing right now. */
|
||
expand_catch (decl);
|
||
if (current_exception_type
|
||
&& TYPE_NEEDS_DESTRUCTOR (current_exception_type))
|
||
{
|
||
/* Make a cleanup for the name-specific exception object now in scope. */
|
||
tree cleanup = maybe_build_cleanup (current_exception_object);
|
||
expand_start_bindings (0);
|
||
expand_decl_cleanup (NULL_TREE, cleanup);
|
||
}
|
||
return decl;
|
||
}
|
||
|
||
void
|
||
cplus_expand_end_catch (for_reraise)
|
||
int for_reraise;
|
||
{
|
||
if (current_exception_type
|
||
&& TYPE_NEEDS_DESTRUCTOR (current_exception_type))
|
||
{
|
||
/* Destroy the specific exception object now in scope. */
|
||
expand_end_bindings (getdecls (), 0, 1);
|
||
}
|
||
if (for_reraise)
|
||
{
|
||
if (! expand_escape_except ())
|
||
abort ();
|
||
}
|
||
else
|
||
{
|
||
if (! expand_end_catch ())
|
||
abort ();
|
||
}
|
||
expand_end_cond ();
|
||
}
|
||
|
||
/* Reraise an exception.
|
||
If EXCEPTIONS is NULL_TREE, it means reraise whatever exception was caught.
|
||
If EXCEPTIONS is an IDENTIFIER_NODE, it means reraise the exception
|
||
object named by EXCEPTIONS. This must be a variable declared in
|
||
an `except' clause.
|
||
If EXCEPTIONS is a TREE_LIST, it is the list of exceptions we are
|
||
willing to reraise. */
|
||
|
||
void
|
||
cplus_expand_reraise (exceptions)
|
||
tree exceptions;
|
||
{
|
||
tree ex_ptr;
|
||
tree ex_object = current_exception_object;
|
||
|
||
if (exceptions && TREE_CODE (exceptions) == IDENTIFIER_NODE)
|
||
{
|
||
/* Don't get tripped up if its TREE_TYPE is `error_mark_node'. */
|
||
ex_object = IDENTIFIER_LOCAL_VALUE (exceptions);
|
||
if (ex_object == NULL_TREE || TREE_CODE (ex_object) != INDIRECT_REF)
|
||
{
|
||
error ("`%s' is not an exception decl", IDENTIFIER_POINTER (exceptions));
|
||
return;
|
||
}
|
||
assert (TREE_CODE (TREE_OPERAND (ex_object, 0)) == VAR_DECL);
|
||
exceptions = NULL_TREE;
|
||
}
|
||
|
||
/* reraise ALL, used by compiler. */
|
||
if (exceptions == NULL_TREE)
|
||
{
|
||
/* Now treat reraise like catch/raise. */
|
||
expand_catch (error_mark_node);
|
||
expand_raise (error_mark_node);
|
||
expand_assignment (EHS_name,
|
||
build (COMPONENT_REF, ptr_type_node,
|
||
current_exception_decl, TREE_OPERAND (EHS_name, 1)), 0, 0);
|
||
expand_assignment (EHS_parms,
|
||
build (COMPONENT_REF, ptr_type_node,
|
||
current_exception_decl, TREE_OPERAND (EHS_parms, 1)), 0, 0);
|
||
if (in_try_block (1) == 0)
|
||
{
|
||
sets_exception_throw_decl = 1;
|
||
expand_assignment (exception_throw_decl, integer_one_node, 0, 0);
|
||
}
|
||
/* Set to zero so that destructor will not be called. */
|
||
expand_assignment (build1 (NOP_EXPR, ptr_type_node, TREE_OPERAND (ex_object, 0)),
|
||
integer_zero_node, 0, 0);
|
||
if (! expand_escape_except ())
|
||
abort ();
|
||
return;
|
||
}
|
||
|
||
ex_ptr = build1 (NOP_EXPR, NULL_TREE, TREE_OPERAND (ex_object, 0));
|
||
|
||
/* reraise from a list of exceptions. */
|
||
while (exceptions)
|
||
{
|
||
tree type = lookup_exception_type (current_class_type, current_class_name,
|
||
exceptions);
|
||
if (type == NULL_TREE)
|
||
{
|
||
error ("`%s' is not an exception type",
|
||
IDENTIFIER_POINTER (TREE_VALUE (exceptions)));
|
||
current_exception_type = NULL_TREE;
|
||
TREE_TYPE (ex_object) = error_mark_node;
|
||
TREE_TYPE (ex_ptr) = error_mark_node;
|
||
}
|
||
else
|
||
{
|
||
current_exception_type = type;
|
||
/* In-place union. */
|
||
TREE_TYPE (ex_object) = type;
|
||
TREE_TYPE (ex_ptr) = TYPE_POINTER_TO (type);
|
||
}
|
||
|
||
/* Now treat reraise like catch/raise. */
|
||
cplus_expand_start_catch (exceptions);
|
||
cplus_expand_raise (exceptions, NULL_TREE, ex_ptr);
|
||
/* Set to zero so that destructor will not be called. */
|
||
if (TREE_TYPE (ex_ptr) != error_mark_node)
|
||
expand_assignment (ex_ptr, integer_zero_node, 0, 0);
|
||
cplus_expand_end_catch (1);
|
||
exceptions = TREE_CHAIN (exceptions);
|
||
}
|
||
/* Don't propagate any unhandled exceptions. */
|
||
expand_expr (call_to_unhandled_exception (), 0, VOIDmode, 0);
|
||
}
|
||
|
||
void
|
||
setup_exception_throw_decl ()
|
||
{
|
||
tree call_to_longjmp, parms;
|
||
|
||
int old = suspend_momentary ();
|
||
|
||
exception_throw_decl = build_decl (VAR_DECL, get_identifier (THROW_NAME), integer_type_node);
|
||
pushdecl (exception_throw_decl);
|
||
parms = tree_cons (NULL_TREE, EHS_handler,
|
||
build_tree_list (0, integer_one_node));
|
||
call_to_longjmp = build_function_call (BILJ, parms);
|
||
|
||
expand_decl (exception_throw_decl);
|
||
expand_decl_cleanup (exception_throw_decl,
|
||
build (COND_EXPR, void_type_node,
|
||
exception_throw_decl,
|
||
call_to_longjmp, integer_zero_node));
|
||
DECL_INITIAL (exception_throw_decl) = integer_zero_node;
|
||
sets_exception_throw_decl = 0;
|
||
resume_momentary (old);
|
||
}
|
||
|
||
void
|
||
init_exception_processing ()
|
||
{
|
||
extern tree unhandled_exception_fndecl;
|
||
tree cname = get_identifier ("ExceptionHandler");
|
||
tree field, chain;
|
||
tree ctor, dtor;
|
||
tree jmp_buf_type = build_array_type (integer_type_node,
|
||
build_index_type (build_int_2 (_JBLEN-1, 0)));
|
||
tree jmp_buf_arg_type = build_pointer_type (integer_type_node);
|
||
|
||
tree parmtypes = hash_tree_chain (jmp_buf_arg_type, NULL_TREE);
|
||
tree setjmp_fndecl, longjmp_fndecl, raise_fndecl;
|
||
|
||
EHS_type = xref_tag (record_type_node, cname, NULL_TREE);
|
||
|
||
push_lang_context (lang_name_c);
|
||
setjmp_fndecl = define_function ("setjmp",
|
||
build_function_type (integer_type_node,
|
||
parmtypes),
|
||
NOT_BUILT_IN, 0);
|
||
BISJ = default_conversion (setjmp_fndecl);
|
||
longjmp_fndecl = define_function ("longjmp",
|
||
build_function_type (integer_type_node,
|
||
hash_tree_chain (jmp_buf_arg_type,
|
||
hash_tree_chain (integer_type_node, NULL_TREE))),
|
||
NOT_BUILT_IN, 0);
|
||
raise_fndecl = define_function ("__raise_exception",
|
||
build_function_type (void_type_node,
|
||
hash_tree_chain (ptr_type_node,
|
||
hash_tree_chain (build_pointer_type (ptr_type_node), NULL_TREE))));
|
||
BILJ = default_conversion (longjmp_fndecl);
|
||
BIR = default_conversion (raise_fndecl);
|
||
BIUE = default_conversion (unhandled_exception_fndecl);
|
||
|
||
pop_lang_context ();
|
||
|
||
/* finish_struct will pop this. */
|
||
pushclass (EHS_type, 0);
|
||
field = build_lang_field_decl (FIELD_DECL, get_identifier ("parms"), ptr_type_node);
|
||
chain = field;
|
||
field = build_lang_field_decl (FIELD_DECL, get_identifier ("name"),
|
||
build_pointer_type (default_function_type));
|
||
TREE_CHAIN (field) = chain;
|
||
chain = field;
|
||
field = build_lang_field_decl (FIELD_DECL, get_identifier ("handler"), jmp_buf_type);
|
||
TREE_CHAIN (field) = chain;
|
||
chain = field;
|
||
field = build_lang_field_decl (FIELD_DECL, get_identifier ("prev"),
|
||
TYPE_POINTER_TO (EHS_type));
|
||
TREE_CHAIN (field) = chain;
|
||
chain = field;
|
||
|
||
ctor = build_lang_decl (FUNCTION_DECL, cname,
|
||
build_cplus_method_type (EHS_type, TYPE_POINTER_TO (EHS_type), void_list_node));
|
||
DECL_CONSTRUCTOR_P (ctor) = 1;
|
||
TREE_STATIC (ctor) = 1;
|
||
TREE_PUBLIC (ctor) = 1;
|
||
grokclassfn (EHS_type, cname, ctor, NO_SPECIAL, 0, 0);
|
||
finish_decl (ctor, 0, 0);
|
||
TREE_CHAIN (ctor) = chain;
|
||
chain = ctor;
|
||
dtor = build_lang_decl (FUNCTION_DECL, cname,
|
||
build_cplus_method_type (EHS_type, TYPE_POINTER_TO (EHS_type), void_list_node));
|
||
TREE_STATIC (dtor) = 1;
|
||
TREE_PUBLIC (dtor) = 1;
|
||
grokclassfn (EHS_type, cname, dtor, DTOR_FLAG, 0, 0);
|
||
finish_decl (dtor, 0, 0);
|
||
TREE_CHAIN (dtor) = chain;
|
||
chain = dtor;
|
||
TYPE_HAS_CONSTRUCTOR (EHS_type) = 1;
|
||
TYPE_HAS_DESTRUCTOR (EHS_type) = 1;
|
||
finish_struct (EHS_type, temp_tree_cons (NULL_TREE, chain, NULL_TREE), 0, 0);
|
||
}
|
||
|
||
void
|
||
init_exception_processing_1 ()
|
||
{
|
||
register tree EHS_id = get_identifier ("exceptionHandlerStack");
|
||
|
||
EHS_decl = IDENTIFIER_GLOBAL_VALUE (EHS_id);
|
||
|
||
/* If we have no other definition, default to library implementation. */
|
||
if (EHS_decl == NULL_TREE)
|
||
{
|
||
EHS_decl = build_decl (VAR_DECL, EHS_id, TYPE_POINTER_TO (EHS_type));
|
||
/* If we don't push this, its definition, should it be encountered,
|
||
will not be seen. */
|
||
EHS_decl = pushdecl (EHS_decl);
|
||
TREE_EXTERNAL (EHS_decl) = 1;
|
||
TREE_STATIC (EHS_decl) = 1;
|
||
TREE_PUBLIC (EHS_decl) = 1;
|
||
finish_decl (EHS_decl, 0, 0);
|
||
}
|
||
else if (TREE_CODE (EHS_decl) != VAR_DECL
|
||
|| TREE_TYPE (EHS_decl) != TYPE_POINTER_TO (EHS_type))
|
||
fatal ("exception handling declarations conflict with compiler's internal model");
|
||
|
||
if (EHS_prev == NULL_TREE)
|
||
{
|
||
register tree EHS_DECL = build1 (INDIRECT_REF, EHS_type, EHS_decl);
|
||
EHS_prev = build_component_ref (EHS_DECL, get_identifier ("prev"), 0, 0);
|
||
EHS_handler = build_component_ref (EHS_DECL, get_identifier ("handler"), 0, 0);
|
||
EHS_parms = build_component_ref (EHS_DECL, get_identifier ("parms"), 0, 0);
|
||
EHS_name = build_component_ref (EHS_DECL, get_identifier ("name"), 0, 0);
|
||
}
|
||
|
||
if (use_gdb_dbx_extensions)
|
||
dbxout_eh_init (EHS_type, EHS_decl);
|
||
}
|