This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[objc] exception handling rewrite; gnu runtime operational
- From: Richard Henderson <rth at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Thu, 17 Jun 2004 18:39:39 -0700
- Subject: [objc] exception handling rewrite; gnu runtime operational
- References: <20040617115524.GA26965@redhat.com>
Andrew Pinski was kind enough to test this out on Darwin. It showed
one buglet, which I fixed.
I've also stolen enough code from libstdc++ and libjava to implement
the exception handling backend bits in libobjc, so the one execute
test in the testsuite now passes with the GNU runtime.
The Objective C "specification" document on Apple's web site is more
like "spec-lite". It has nothing to say about how exceptions in the
language actually work. My guess from looking at the pattern of
calls generated for the NeXT runtime is ObjC is more like Java than
C++ in that there are never nested or pending exceptions. Thus there
is never a need to manage a stack of exceptions or anything. I have no
idea what ObjC is supposed to do with out-of-memory conditions; I did
not bother doing some emergency eh allocation pool as we do for C++.
According to the comments at the beginning of Object.m, the GNU runtime
is already sort of disturbingly tied to strings when matching up with
class objects, so the scheme I chose to use string pointers in the EH
catch type info should be about as good as can be done at present.
I have not implemented the two calls needed by @synchronized. I have
no idea what sort of existing thread support exists in Objective C.
Anyway, this bit needs no familiarity with exception handling at all,
so I shall leave it to someone who actually cares to implement.
r~
gcc/
* c-common.c (flag_objc_sjlj_exceptions): New.
* c-common.h (flag_objc_sjlj_exceptions): Declare.
* c-opts.c (c_common_handle_option): Set it.
(c_common_post_options): Handle interation of different
objective-c exception and runtime switches.
* c-decl.c (c_eh_initialized_p): New.
(finish_decl): Use it instead of local eh_initialized_p.
* c-parse.in (nested_function, notype_nested_function): Record
the result of compstmt.
(compstmt_or_error): Likewise.
(compstmt): Don't add_stmt the result.
(stmt): Don't return anything. Rewrite objc try and sync rules.
(objc_try_stmt, objc_catch_list): Remove.
(objc_catch_block, objc_finally_block): Remove.
(objc_catch_prefix, objc_catch_clause, objc_opt_catch_list): New.
(objc_try_catch_clause, objc_finally_clause): New.
(objc_try_catch_stmt): Rewrite.
* c-tree.h (c_eh_initialized_p): Declare.
* c-opt (fobjc-sjlj-exceptions): New.
* except.c (output_function_exception_table): Don't call cgraph
on non-decls.
* objc/objc-act.c (UTAG_EXCDATA_VAR, UTAG_CAUGHTEXC_VAR,
UTAG_RETHROWEXC_VAR, UTAG_EVALONCE_VAR, struct val_stack,
catch_count_stack, exc_binding_stack, if_nesting_count,
blk_nesting_count, objc_enter_block, objc_exit_block,
objc_declare_variable, val_stack_push, val_stack_pop,
objc_build_try_enter_fragment, objc_build_extract_expr,
objc_build_try_exit_fragment, objc_build_extract_fragment,
objc_build_try_prologue, objc_build_try_epilogue,
objc_build_catch_stmt, objc_build_catch_epilogue,
objc_build_finally_prologue, objc_build_finally_epilogue,
objc_build_try_catch_finally_stmt, objc_build_synchronized_prologue,
objc_build_synchronized_epilogue): Remove.
(objc_create_temporary_var, struct objc_try_context, cur_try_context,
objc_eh_runtime_type, objc_init_exceptions, objc_build_exc_ptr,
next_sjlj_build_try_exit, next_sjlj_build_enter_and_setjmp,
next_sjlj_build_exc_extract, next_sjlj_build_catch_list,
next_sjlj_build_try_catch_finally, objc_begin_try_stmt,
objc_begin_catch_clause, objc_finish_catch_clause,
objc_build_finally_clause, objc_finish_try_stmt,
objc_build_synchronized): New.
(objc_is_object_id, objc_is_class_id): New.
(objc_comptypes): Use them.
(build_next_objc_exception_stuff): Break NeXT sjlj out from
build_objc_exception_stuff.
(synth_module_prologue): Update to match.
(objc_build_throw_stmt): Use cur_try_context to decide if
we're in a @catch.
* objc/objc-act.h: Update prototypes.
(OCTI_EXCEPTION_BLK_STACK, objc_exception_block_stack): Remove.
gcc/testsuite/
* objc.dg/sync-1.m: New.
* objc.dg/try-catch-1.m: Don't force next runtime.
* objc.dg/try-catch-3.m, objc.dg/try-catch-4.m: Likewise.
* objc.dg/try-catch-2.m: Likewise. Enable everywhere. Remove
shadowed catch clause.
* objc.dg/try-catch-5.m: New.
libobjc/
* exception.c: New file.
* Makefile.in (exception.lo): New.
(OBJS): Add it.
Index: gcc/c-common.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-common.c,v
retrieving revision 1.515
diff -c -p -d -r1.515 c-common.c
*** gcc/c-common.c 17 Jun 2004 01:23:57 -0000 1.515
--- gcc/c-common.c 18 Jun 2004 00:57:02 -0000
*************** int flag_nil_receivers = 1;
*** 361,366 ****
--- 361,369 ----
@try, etc.) in source code. */
int flag_objc_exceptions = 0;
+ /* Nonzero means that we generate NeXT setjmp based exceptions. */
+ int flag_objc_sjlj_exceptions = -1;
+
/* Nonzero means that code generation will be altered to support
"zero-link" execution. This currently affects ObjC only, but may
affect other languages in the future. */
Index: gcc/c-common.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-common.h,v
retrieving revision 1.236
diff -c -p -d -r1.236 c-common.h
*** gcc/c-common.h 17 Jun 2004 01:23:57 -0000 1.236
--- gcc/c-common.h 18 Jun 2004 00:57:02 -0000
*************** extern int flag_nil_receivers;
*** 321,326 ****
--- 321,329 ----
@try, etc.) in source code. */
extern int flag_objc_exceptions;
+ /* Nonzero means that we generate NeXT setjmp based exceptions. */
+ extern int flag_objc_sjlj_exceptions;
+
/* Nonzero means that code generation will be altered to support
"zero-link" execution. This currently affects ObjC only, but may
affect other languages in the future. */
Index: gcc/c-decl.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-decl.c,v
retrieving revision 1.509
diff -c -p -d -r1.509 c-decl.c
*** gcc/c-decl.c 16 Jun 2004 01:21:20 -0000 1.509
--- gcc/c-decl.c 18 Jun 2004 00:57:02 -0000
*************** enum decl_context
*** 71,81 ****
/* Nonzero if we have seen an invalid cross reference
to a struct, union, or enum, but not yet printed the message. */
-
tree pending_invalid_xref;
/* File and line to appear in the eventual error message. */
location_t pending_invalid_xref_location;
/* While defining an enum type, this is 1 plus the last enumerator
constant value. Note that will do not have to save this or `enum_overflow'
around nested function definition since such a definition could only
--- 71,84 ----
/* Nonzero if we have seen an invalid cross reference
to a struct, union, or enum, but not yet printed the message. */
tree pending_invalid_xref;
+
/* File and line to appear in the eventual error message. */
location_t pending_invalid_xref_location;
+ /* True means we've initialized exception handling. */
+ bool c_eh_initialized_p;
+
/* While defining an enum type, this is 1 plus the last enumerator
constant value. Note that will do not have to save this or `enum_overflow'
around nested function definition since such a definition could only
*************** finish_decl (tree decl, tree init, tree
*** 2982,2989 ****
tree attr = lookup_attribute ("cleanup", DECL_ATTRIBUTES (decl));
if (attr)
{
- static bool eh_initialized_p;
-
tree cleanup_id = TREE_VALUE (TREE_VALUE (attr));
tree cleanup_decl = lookup_name (cleanup_id);
tree cleanup;
--- 2985,2990 ----
*************** finish_decl (tree decl, tree init, tree
*** 2998,3006 ****
TREE_USED (cleanup_decl) = 1;
/* Initialize EH, if we've been told to do so. */
! if (flag_exceptions && !eh_initialized_p)
{
! eh_initialized_p = true;
eh_personality_libfunc
= init_one_libfunc (USING_SJLJ_EXCEPTIONS
? "__gcc_personality_sj0"
--- 2999,3007 ----
TREE_USED (cleanup_decl) = 1;
/* Initialize EH, if we've been told to do so. */
! if (flag_exceptions && !c_eh_initialized_p)
{
! c_eh_initialized_p = true;
eh_personality_libfunc
= init_one_libfunc (USING_SJLJ_EXCEPTIONS
? "__gcc_personality_sj0"
Index: gcc/c-opts.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-opts.c,v
retrieving revision 1.115
diff -c -p -d -r1.115 c-opts.c
*** gcc/c-opts.c 5 Jun 2004 20:58:06 -0000 1.115
--- gcc/c-opts.c 18 Jun 2004 00:57:02 -0000
*************** c_common_handle_option (size_t scode, co
*** 863,868 ****
--- 863,872 ----
flag_objc_exceptions = value;
break;
+ case OPT_fobjc_sjlj_exceptions:
+ flag_objc_sjlj_exceptions = value;
+ break;
+
case OPT_foperator_names:
cpp_opts->operator_names = value;
break;
*************** c_common_post_options (const char **pfil
*** 1109,1114 ****
--- 1113,1124 ----
flag_inline_functions = 0;
}
+ /* Default to ObjC sjlj exception handling if NeXT runtime. */
+ if (flag_objc_sjlj_exceptions < 0)
+ flag_objc_sjlj_exceptions = flag_next_runtime;
+ if (flag_objc_exceptions && !flag_objc_sjlj_exceptions)
+ flag_exceptions = 1;
+
/* -Wextra implies -Wsign-compare, but not if explicitly
overridden. */
if (warn_sign_compare == -1)
Index: gcc/c-parse.in
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-parse.in,v
retrieving revision 1.214
diff -c -p -d -r1.214 c-parse.in
*** gcc/c-parse.in 17 Jun 2004 17:41:40 -0000 1.214
--- gcc/c-parse.in 18 Jun 2004 00:57:02 -0000
*************** do { \
*** 251,257 ****
%type <ttype> CLASSNAME OBJECTNAME OBJC_STRING
%type <ttype> superclass
- %type <itype> objc_try_catch_stmt objc_finally_block
@@end_ifobjc
%{
--- 251,256 ----
*************** designator:
*** 1515,1521 ****
;
nested_function:
! declarator
{ if (pedantic)
pedwarn ("ISO C forbids nested functions");
--- 1514,1520 ----
;
nested_function:
! declarator
{ if (pedantic)
pedwarn ("ISO C forbids nested functions");
*************** nested_function:
*** 1527,1551 ****
YYERROR1;
}
}
! old_style_parm_decls save_location
{ tree decl = current_function_decl;
DECL_SOURCE_LOCATION (decl) = $4;
store_parm_decls (); }
! /* This used to use compstmt_or_error.
! That caused a bug with input `f(g) int g {}',
! where the use of YYERROR1 above caused an error
! which then was handled by compstmt_or_error.
! There followed a repeated execution of that same rule,
! which called YYERROR1 again, and so on. */
! compstmt
{ tree decl = current_function_decl;
finish_function ();
pop_function_context ();
add_decl_stmt (decl); }
;
notype_nested_function:
! notype_declarator
{ if (pedantic)
pedwarn ("ISO C forbids nested functions");
--- 1526,1550 ----
YYERROR1;
}
}
! old_style_parm_decls save_location
{ tree decl = current_function_decl;
DECL_SOURCE_LOCATION (decl) = $4;
store_parm_decls (); }
! /* This used to use compstmt_or_error. That caused a bug with
! input `f(g) int g {}', where the use of YYERROR1 above caused
! an error which then was handled by compstmt_or_error. There
! followed a repeated execution of that same rule, which called
! YYERROR1 again, and so on. */
! compstmt
{ tree decl = current_function_decl;
+ add_stmt ($6);
finish_function ();
pop_function_context ();
add_decl_stmt (decl); }
;
notype_nested_function:
! notype_declarator
{ if (pedantic)
pedwarn ("ISO C forbids nested functions");
*************** notype_nested_function:
*** 1557,1574 ****
YYERROR1;
}
}
! old_style_parm_decls save_location
{ tree decl = current_function_decl;
DECL_SOURCE_LOCATION (decl) = $4;
store_parm_decls (); }
! /* This used to use compstmt_or_error.
! That caused a bug with input `f(g) int g {}',
! where the use of YYERROR1 above caused an error
! which then was handled by compstmt_or_error.
! There followed a repeated execution of that same rule,
! which called YYERROR1 again, and so on. */
! compstmt
{ tree decl = current_function_decl;
finish_function ();
pop_function_context ();
add_decl_stmt (decl); }
--- 1556,1573 ----
YYERROR1;
}
}
! old_style_parm_decls save_location
{ tree decl = current_function_decl;
DECL_SOURCE_LOCATION (decl) = $4;
store_parm_decls (); }
! /* This used to use compstmt_or_error. That caused a bug with
! input `f(g) int g {}', where the use of YYERROR1 above caused
! an error which then was handled by compstmt_or_error. There
! followed a repeated execution of that same rule, which called
! YYERROR1 again, and so on. */
! compstmt
{ tree decl = current_function_decl;
+ add_stmt ($6);
finish_function ();
pop_function_context ();
add_decl_stmt (decl); }
*************** label_decl:
*** 2029,2035 ****
It causes syntax errors to ignore to the next openbrace. */
compstmt_or_error:
compstmt
! {}
| error compstmt
;
--- 2028,2034 ----
It causes syntax errors to ignore to the next openbrace. */
compstmt_or_error:
compstmt
! { add_stmt ($1); }
| error compstmt
;
*************** compstmt_primary_start:
*** 2060,2067 ****
;
compstmt: compstmt_start compstmt_nostart
! { add_stmt (c_end_compound_stmt ($1, true));
! $$ = NULL_TREE; }
;
if_prefix:
--- 2059,2065 ----
;
compstmt: compstmt_start compstmt_nostart
! { $$ = c_end_compound_stmt ($1, true); }
;
if_prefix:
*************** xexpr:
*** 2226,2262 ****
/* Parse a single real statement, not including any labels. */
stmt:
compstmt
! { stmt_count++; $$ = $1; }
| expr ';'
! { stmt_count++;
! $$ = c_expand_expr_stmt ($1); }
| c99_block_start select_or_iter_stmt
! { add_stmt (c_end_compound_stmt ($1, flag_isoc99));
! $$ = NULL_TREE; }
| BREAK ';'
{ stmt_count++;
if (!(c_in_iteration_stmt || c_in_case_stmt))
! {
! error ("break statement not within loop or switch");
! $$ = NULL_TREE;
! }
else
! $$ = add_stmt (build_break_stmt ()); }
| CONTINUE ';'
{ stmt_count++;
if (!c_in_iteration_stmt)
! {
! error ("continue statement not within a loop");
! $$ = NULL_TREE;
! }
else
! $$ = add_stmt (build_continue_stmt ()); }
| RETURN ';'
! { stmt_count++;
! $$ = c_expand_return (NULL_TREE); }
| RETURN expr ';'
! { stmt_count++;
! $$ = c_expand_return ($2); }
| asm_stmt
| GOTO identifier ';'
{ tree decl;
--- 2224,2250 ----
/* Parse a single real statement, not including any labels. */
stmt:
compstmt
! { stmt_count++; add_stmt ($1); }
| expr ';'
! { stmt_count++; c_expand_expr_stmt ($1); }
| c99_block_start select_or_iter_stmt
! { add_stmt (c_end_compound_stmt ($1, flag_isoc99)); }
| BREAK ';'
{ stmt_count++;
if (!(c_in_iteration_stmt || c_in_case_stmt))
! error ("break statement not within loop or switch");
else
! add_stmt (build_break_stmt ()); }
| CONTINUE ';'
{ stmt_count++;
if (!c_in_iteration_stmt)
! error ("continue statement not within a loop");
else
! add_stmt (build_continue_stmt ()); }
| RETURN ';'
! { stmt_count++; c_expand_return (NULL_TREE); }
| RETURN expr ';'
! { stmt_count++; c_expand_return ($2); }
| asm_stmt
| GOTO identifier ';'
{ tree decl;
*************** stmt:
*** 2265,2335 ****
if (decl != 0)
{
TREE_USED (decl) = 1;
! $$ = add_stmt (build_stmt (GOTO_EXPR, decl));
}
- else
- $$ = NULL_TREE;
}
| GOTO '*' expr ';'
{ if (pedantic)
pedwarn ("ISO C forbids `goto *expr;'");
stmt_count++;
$3 = convert (ptr_type_node, $3);
! $$ = add_stmt (build_stmt (GOTO_EXPR, $3)); }
| ';'
! { $$ = NULL_TREE; }
@@ifobjc
| AT_THROW expr ';'
! { stmt_count++;
! $$ = objc_build_throw_stmt ($2);
! }
| AT_THROW ';'
! { stmt_count++;
! $$ = objc_build_throw_stmt (NULL_TREE);
! }
| objc_try_catch_stmt
! { objc_build_finally_prologue (); }
! objc_finally_block
! { $$ = objc_build_try_catch_finally_stmt ($1, $3); }
! | AT_SYNCHRONIZED '(' expr ')'
! { objc_build_synchronized_prologue ($3); }
! compstmt
! { $$ = objc_build_synchronized_epilogue (); }
;
! objc_try_catch_stmt:
! objc_try_stmt
! { objc_build_try_epilogue (1); }
! objc_catch_list
! { objc_build_catch_epilogue (); $$ = 1; }
! | objc_try_stmt
! { objc_build_try_epilogue (0); $$ = 0; }
;
! objc_try_stmt:
! AT_TRY
! { objc_build_try_prologue (); }
! compstmt
;
! objc_catch_list:
! objc_catch_list objc_catch_block
! | objc_catch_block
;
! objc_catch_block:
! AT_CATCH '(' parm ')'
! { objc_build_catch_stmt ($3); }
! compstmt
! { stmt_count++; }
;
! objc_finally_block:
! AT_FINALLY compstmt
! { $$ = 1; }
! | /* NULL */
! { $$ = 0; }
@@end_ifobjc
;
--- 2253,2313 ----
if (decl != 0)
{
TREE_USED (decl) = 1;
! add_stmt (build_stmt (GOTO_EXPR, decl));
}
}
| GOTO '*' expr ';'
{ if (pedantic)
pedwarn ("ISO C forbids `goto *expr;'");
stmt_count++;
$3 = convert (ptr_type_node, $3);
! add_stmt (build_stmt (GOTO_EXPR, $3)); }
| ';'
! { }
@@ifobjc
| AT_THROW expr ';'
! { stmt_count++; objc_build_throw_stmt ($2); }
| AT_THROW ';'
! { stmt_count++; objc_build_throw_stmt (NULL_TREE); }
| objc_try_catch_stmt
! { }
! | AT_SYNCHRONIZED '(' expr ')' save_location compstmt
! { stmt_count++; objc_build_synchronized ($5, $3, $6); }
;
! objc_catch_prefix:
! AT_CATCH '(' parm ')'
! { objc_begin_catch_clause ($3); }
;
+ objc_catch_clause:
+ objc_catch_prefix '{' compstmt_nostart
+ { objc_finish_catch_clause (); }
+ | objc_catch_prefix '{' error '}'
+ { objc_finish_catch_clause (); }
+ ;
! objc_opt_catch_list:
! /* empty */
! | objc_opt_catch_list objc_catch_clause
;
! objc_try_catch_clause:
! AT_TRY save_location compstmt
! { stmt_count++; objc_begin_try_stmt ($2, $3); }
! objc_opt_catch_list
;
! objc_finally_clause:
! AT_FINALLY save_location compstmt
! { objc_build_finally_clause ($2, $3); }
;
! objc_try_catch_stmt:
! objc_try_catch_clause
! { objc_finish_try_stmt (); }
! | objc_try_catch_clause objc_finally_clause
! { objc_finish_try_stmt (); }
@@end_ifobjc
;
Index: gcc/c-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-tree.h,v
retrieving revision 1.152
diff -c -p -d -r1.152 c-tree.h
*** gcc/c-tree.h 17 Jun 2004 01:06:03 -0000 1.152
--- gcc/c-tree.h 18 Jun 2004 00:57:02 -0000
*************** extern int system_header_p;
*** 298,303 ****
--- 298,306 ----
extern bool c_override_global_bindings_to_false;
+ /* True means we've initialized exception handling. */
+ extern bool c_eh_initialized_p;
+
/* In c-decl.c */
extern void c_finish_incomplete_decl (tree);
extern void *get_current_scope (void);
Index: gcc/c.opt
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c.opt,v
retrieving revision 1.26
diff -c -p -d -r1.26 c.opt
*** gcc/c.opt 14 Jun 2004 14:17:50 -0000 1.26
--- gcc/c.opt 18 Jun 2004 00:57:03 -0000
*************** fobjc-exceptions
*** 584,589 ****
--- 584,593 ----
ObjC ObjC++
Enable Objective-C exception and synchronization syntax
+ fobjc-sjlj-exceptions
+ ObjC ObjC++
+ Enable Objective-C setjmp exception handling runtime
+
foperator-names
C++ ObjC++
Recognize C++ kewords like \"compl\" and \"xor\"
Index: gcc/except.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/except.c,v
retrieving revision 1.275
diff -c -p -d -r1.275 except.c
*** gcc/except.c 17 Jun 2004 19:53:55 -0000 1.275
--- gcc/except.c 18 Jun 2004 00:57:03 -0000
*************** output_function_exception_table (void)
*** 4085,4093 ****
if (TREE_CODE (type) == ADDR_EXPR)
{
type = TREE_OPERAND (type, 0);
! node = cgraph_varpool_node (type);
! if (node)
! cgraph_varpool_mark_needed_node (node);
}
else if (TREE_CODE (type) != INTEGER_CST)
abort ();
--- 4085,4096 ----
if (TREE_CODE (type) == ADDR_EXPR)
{
type = TREE_OPERAND (type, 0);
! if (TREE_CODE (type) == VAR_DECL)
! {
! node = cgraph_varpool_node (type);
! if (node)
! cgraph_varpool_mark_needed_node (node);
! }
}
else if (TREE_CODE (type) != INTEGER_CST)
abort ();
Index: gcc/objc/objc-act.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/objc/objc-act.c,v
retrieving revision 1.221
diff -c -p -d -r1.221 objc-act.c
*** gcc/objc/objc-act.c 17 Jun 2004 01:24:06 -0000 1.221
--- gcc/objc/objc-act.c 18 Jun 2004 00:57:03 -0000
*************** Boston, MA 02111-1307, USA. */
*** 63,68 ****
--- 63,70 ----
#include "target.h"
#include "diagnostic.h"
#include "cgraph.h"
+ #include "tree-iterator.h"
+ #include "libfuncs.h"
/* This is the default way of generating a method name. */
/* I am not sure it is really correct.
*************** static void build_selector_translation_t
*** 134,146 ****
static tree objc_add_static_instance (tree, tree);
static void build_objc_exception_stuff (void);
! static tree objc_declare_variable (enum rid, tree, tree, tree);
! static tree objc_enter_block (void);
! static tree objc_exit_block (void);
! static void objc_build_try_enter_fragment (void);
! static void objc_build_try_exit_fragment (void);
! static void objc_build_extract_fragment (void);
! static tree objc_build_extract_expr (void);
static tree build_ivar_template (void);
static tree build_method_template (void);
--- 136,142 ----
static tree objc_add_static_instance (tree, tree);
static void build_objc_exception_stuff (void);
! static void build_next_objc_exception_stuff (void);
static tree build_ivar_template (void);
static tree build_method_template (void);
*************** static const char *default_constant_stri
*** 365,387 ****
#define TAG_RETURN_STRUCT "objc_return_struct"
#define UTAG_EXCDATA "_objc_exception_data"
- #define UTAG_EXCDATA_VAR "_stackExceptionData"
- #define UTAG_CAUGHTEXC_VAR "_caughtException"
- #define UTAG_RETHROWEXC_VAR "_rethrowException"
- #define UTAG_EVALONCE_VAR "_eval_once"
-
- struct val_stack {
- long val;
- struct val_stack *next;
- };
- static struct val_stack *catch_count_stack, *exc_binding_stack;
-
- /* useful for debugging */
- static int if_nesting_count;
- static int blk_nesting_count;
-
- static void val_stack_push (struct val_stack **, long);
- static void val_stack_pop (struct val_stack **);
/* The OCTI_... enumeration itself is in objc/objc-act.h. */
tree objc_global_trees[OCTI_MAX];
--- 361,366 ----
*************** lookup_protocol_in_reflist (tree rproto_
*** 626,631 ****
--- 605,624 ----
return 0;
}
+ /* Return true if TYPE is 'id'. */
+
+ static bool
+ objc_is_object_id (tree type)
+ {
+ return OBJC_TYPE_NAME (type) == objc_object_id;
+ }
+
+ static bool
+ objc_is_class_id (tree type)
+ {
+ return OBJC_TYPE_NAME (type) == objc_class_id;
+ }
+
/* Return 1 if LHS and RHS are compatible types for assignment or
various other operations. Return 0 if they are incompatible, and
return -1 if we choose to not decide (because the types are really
*************** objc_comptypes (tree lhs, tree rhs, int
*** 777,788 ****
return 1;
}
/* <Protocol> = id */
! else if (OBJC_TYPE_NAME (TREE_TYPE (rhs)) == objc_object_id)
{
return 1;
}
/* <Protocol> = Class */
! else if (OBJC_TYPE_NAME (TREE_TYPE (rhs)) == objc_class_id)
{
return 0;
}
--- 770,781 ----
return 1;
}
/* <Protocol> = id */
! else if (objc_is_object_id (TREE_TYPE (rhs)))
{
return 1;
}
/* <Protocol> = Class */
! else if (objc_is_class_id (TREE_TYPE (rhs)))
{
return 0;
}
*************** objc_comptypes (tree lhs, tree rhs, int
*** 854,865 ****
return 0;
}
/* id = <Protocol> */
! else if (OBJC_TYPE_NAME (TREE_TYPE (lhs)) == objc_object_id)
{
return 1;
}
/* Class = <Protocol> */
! else if (OBJC_TYPE_NAME (TREE_TYPE (lhs)) == objc_class_id)
{
return 0;
}
--- 847,858 ----
return 0;
}
/* id = <Protocol> */
! else if (objc_is_object_id (TREE_TYPE (lhs)))
{
return 1;
}
/* Class = <Protocol> */
! else if (objc_is_class_id (TREE_TYPE (lhs)))
{
return 0;
}
*************** objc_comptypes (tree lhs, tree rhs, int
*** 892,907 ****
'Object *o = [[Object alloc] init]; falls
in the case <class> * = `id'.
*/
! if ((OBJC_TYPE_NAME (lhs) == objc_object_id && TYPED_OBJECT (rhs))
! || (OBJC_TYPE_NAME (rhs) == objc_object_id && TYPED_OBJECT (lhs)))
return 1;
/* `id' = `Class', `Class' = `id' */
! else if ((OBJC_TYPE_NAME (lhs) == objc_object_id
! && OBJC_TYPE_NAME (rhs) == objc_class_id)
! || (OBJC_TYPE_NAME (lhs) == objc_class_id
! && OBJC_TYPE_NAME (rhs) == objc_object_id))
return 1;
/* `Class' != `<class> *' && `<class> *' != `Class'! */
--- 885,898 ----
'Object *o = [[Object alloc] init]; falls
in the case <class> * = `id'.
*/
! if ((objc_is_object_id (lhs) && TYPED_OBJECT (rhs))
! || (objc_is_object_id (rhs) && TYPED_OBJECT (lhs)))
return 1;
/* `id' = `Class', `Class' = `id' */
! else if ((objc_is_object_id (lhs) && objc_is_class_id (rhs))
! || (objc_is_class_id (lhs) && objc_is_object_id (rhs)))
return 1;
/* `Class' != `<class> *' && `<class> *' != `Class'! */
*************** synth_module_prologue (void)
*** 1273,1280 ****
= builtin_function (TAG_GETMETACLASS, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE);
build_super_template ();
if (flag_next_runtime)
! build_objc_exception_stuff ();
/* static SEL _OBJC_SELECTOR_TABLE[]; */
--- 1264,1272 ----
= builtin_function (TAG_GETMETACLASS, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE);
build_super_template ();
+ build_objc_exception_stuff ();
if (flag_next_runtime)
! build_next_objc_exception_stuff ();
/* static SEL _OBJC_SELECTOR_TABLE[]; */
*************** get_class_ivars (tree interface, int raw
*** 2682,3244 ****
}
static tree
! objc_enter_block (void)
{
! tree block;
!
! #ifdef OBJCPLUS
! block = begin_compound_stmt (0);
! #else
! block = c_begin_compound_stmt (1);
! #endif
!
! objc_exception_block_stack = tree_cons (NULL_TREE, block,
! objc_exception_block_stack);
! blk_nesting_count++;
! return block;
}
! static tree
! objc_exit_block (void)
{
! tree block = TREE_VALUE (objc_exception_block_stack);
! objc_exception_block_stack = TREE_CHAIN (objc_exception_block_stack);
! objc_clear_super_receiver ();
! #ifdef OBJCPLUS
! finish_compound_stmt (block);
! #else
! block = c_end_compound_stmt (block, 1);
! #endif
! blk_nesting_count--;
! return block;
! }
static tree
! objc_declare_variable (enum rid scspec, tree name, tree type, tree init)
{
! tree decl;
!
! type = tree_cons (NULL_TREE, type,
! tree_cons (NULL_TREE, ridpointers[(int) scspec],
! NULL_TREE));
! TREE_STATIC (type) = 1;
! decl = start_decl (name, type, (init != NULL_TREE), NULL_TREE);
! finish_decl (decl, init, NULL_TREE);
! /* This prevents `unused variable' warnings when compiling with -Wall. */
! TREE_USED (decl) = 1;
! DECL_ARTIFICIAL (decl) = 1;
! return decl;
}
! tree
! objc_build_throw_stmt (tree throw_expr)
{
! tree func_params;
if (!flag_objc_exceptions)
! fatal_error ("Use `-fobjc-exceptions' to enable Objective-C exception syntax");
!
! if (!throw_expr && objc_caught_exception)
! throw_expr = TREE_VALUE (objc_caught_exception);
! if (!throw_expr)
{
! error ("`@throw;' (rethrow) used outside of a `@catch' block");
! return error_mark_node;
}
-
- func_params = tree_cons (NULL_TREE, throw_expr, NULL_TREE);
-
- assemble_external (objc_exception_throw_decl);
- return c_expand_expr_stmt (build_function_call (objc_exception_throw_decl,
- func_params));
}
! static void
! val_stack_push (struct val_stack **nc, long val)
{
! struct val_stack *new_elem = xmalloc (sizeof (struct val_stack));
! new_elem->val = val;
! new_elem->next = *nc;
! *nc = new_elem;
}
! static void
! val_stack_pop (struct val_stack **nc)
{
! struct val_stack *old_elem = *nc;
! *nc = old_elem->next;
! free (old_elem);
}
! static void
! objc_build_try_enter_fragment (void)
! {
! /* objc_exception_try_enter(&_stackExceptionData);
! if (!_setjmp(&_stackExceptionData.buf)) { */
! tree func_params, cond;
! func_params
! = tree_cons (NULL_TREE,
! build_unary_op (ADDR_EXPR,
! TREE_VALUE (objc_stack_exception_data),
! 0),
! NULL_TREE);
! assemble_external (objc_exception_try_enter_decl);
! c_expand_expr_stmt (build_function_call
! (objc_exception_try_enter_decl, func_params));
! #ifdef OBJCPLUS
! /* Um, C and C++ have very different statement construction functions.
! Partly because different scoping rules are in effect, but partly
! because of how their parsers are constructed. I highly recommend
! simply constructing the statements by hand here. You don't need
! any of the ancilliary tracking necessary for user parsing bits anyway. */
! #error
! #endif
! c_begin_if_stmt ();
! if_nesting_count++;
! /* If <setjmp.h> has been included, the _setjmp prototype has
! acquired a real, breathing type for its parameter. Cast our
! argument to that type. */
! func_params
! = tree_cons (NULL_TREE,
! build_c_cast (TYPE_ARG_TYPES (TREE_TYPE (objc_setjmp_decl))
! ? TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (objc_setjmp_decl)))
! : ptr_type_node,
! build_unary_op
! (ADDR_EXPR,
! build_component_ref (TREE_VALUE (objc_stack_exception_data),
! get_identifier ("buf")), 0)),
! NULL_TREE);
! assemble_external (objc_setjmp_decl);
! cond = build_unary_op (TRUTH_NOT_EXPR,
! build_function_call (objc_setjmp_decl, func_params),
! 0);
! c_finish_if_cond (cond, 0, 0);
! objc_enter_block ();
}
static tree
! objc_build_extract_expr (void)
{
! /* ... = objc_exception_extract(&_stackExceptionData); */
! tree func_params
! = tree_cons (NULL_TREE,
! build_unary_op (ADDR_EXPR,
! TREE_VALUE (objc_stack_exception_data), 0),
! NULL_TREE);
! assemble_external (objc_exception_extract_decl);
! return build_function_call (objc_exception_extract_decl, func_params);
}
! static void
! objc_build_try_exit_fragment (void)
! {
! /* objc_exception_try_exit(&_stackExceptionData); */
!
! tree func_params
! = tree_cons (NULL_TREE,
! build_unary_op (ADDR_EXPR,
! TREE_VALUE (objc_stack_exception_data), 0),
! NULL_TREE);
!
! assemble_external (objc_exception_try_exit_decl);
! c_expand_expr_stmt (build_function_call (objc_exception_try_exit_decl,
! func_params));
! }
! static void
! objc_build_extract_fragment (void)
{
! /* } else {
! _rethrowException = objc_exception_extract(&_stackExceptionData);
! } */
!
! c_finish_then (objc_exit_block ());
! c_begin_else (0);
! objc_enter_block ();
! c_expand_expr_stmt (build_modify_expr
! (TREE_VALUE (objc_rethrow_exception),
! NOP_EXPR,
! objc_build_extract_expr ()));
! c_finish_else (objc_exit_block ());
! c_finish_if_stmt (1);
! if_nesting_count--;
! }
! tree
! objc_build_try_prologue (void)
! {
! /* { // new scope
! struct _objc_exception_data _stackExceptionData;
! volatile id _rethrowException = nil;
! { // begin TRY-CATCH scope
! objc_exception_try_enter(&_stackExceptionData);
! if (!_setjmp(&_stackExceptionData.buf)) { */
! tree try_catch_block;
! if (!flag_objc_exceptions)
! fatal_error ("Use `-fobjc-exceptions' to enable Objective-C exception syntax");
! objc_mark_locals_volatile ((void *)(exc_binding_stack
! ? exc_binding_stack->val
! : 0));
! objc_enter_block ();
! objc_stack_exception_data
! = tree_cons (NULL_TREE,
! objc_declare_variable (RID_AUTO,
! get_identifier (UTAG_EXCDATA_VAR),
! xref_tag (RECORD_TYPE,
! get_identifier (UTAG_EXCDATA)),
! NULL_TREE),
! objc_stack_exception_data);
! objc_rethrow_exception = tree_cons (NULL_TREE,
! objc_declare_variable (RID_VOLATILE,
! get_identifier (UTAG_RETHROWEXC_VAR),
! id_type,
! build_int_2 (0, 0)),
! objc_rethrow_exception);
! try_catch_block = objc_enter_block ();
! val_stack_push (&exc_binding_stack, (long) get_current_scope ());
! objc_build_try_enter_fragment ();
! return try_catch_block;
}
! void
! objc_build_try_epilogue (int also_catch_prologue)
! {
! if (also_catch_prologue)
! {
! /* } else {
! register id _caughtException = objc_exception_extract( &_stackExceptionData);
! objc_exception_try_enter(&_stackExceptionData);
! if(!_setjmp(&_stackExceptionData.buf)) {
! if (0) { */
!
! c_finish_then (objc_exit_block ());
!
! c_begin_else (0);
! objc_enter_block ();
! objc_caught_exception
! = tree_cons (NULL_TREE,
! objc_declare_variable (RID_REGISTER,
! get_identifier (UTAG_CAUGHTEXC_VAR),
! id_type,
! objc_build_extract_expr ()),
! objc_caught_exception);
! objc_build_try_enter_fragment ();
! val_stack_push (&catch_count_stack, 1);
! c_begin_if_stmt ();
! if_nesting_count++;
! c_finish_if_cond (boolean_false_node, 0, 0);
! objc_enter_block ();
! /* Start a new chain of @catch statements for this @try. */
! objc_catch_type = tree_cons (objc_catch_type, NULL_TREE, NULL_TREE);
! }
! else
! { /* !also_catch_prologue */
! /* } else {
! _rethrowException = objc_exception_extract( &_stackExceptionData);
! }
! } */
! objc_build_extract_fragment ();
! objc_exit_block ();
! }
! }
! void
! objc_build_catch_stmt (tree catch_expr)
{
! /* } else if (objc_exception_match(objc_get_class("SomeClass"), _caughtException)) {
! register SomeClass *e = _caughtException; */
! tree cond, func_params, prev_catch, var_name, var_type;
! int catch_id;
! #ifndef OBJCPLUS
! /* Yet another C/C++ impedance mismatch. */
! catch_expr = TREE_PURPOSE (catch_expr);
! #endif
! var_name = TREE_VALUE (catch_expr);
! var_type = TREE_VALUE (TREE_PURPOSE (catch_expr));
! if (TREE_CODE (var_name) == INDIRECT_REF)
! var_name = TREE_OPERAND (var_name, 0);
! if (TREE_CODE (var_type) == TYPE_DECL
! || TREE_CODE (var_type) == POINTER_TYPE)
! var_type = TREE_TYPE (var_type);
! catch_id = (var_type == TREE_TYPE (id_type));
! if (!flag_objc_exceptions)
! fatal_error ("Use `-fobjc-exceptions' to enable Objective-C exception syntax");
! if (!(catch_id || TYPED_OBJECT (var_type)))
! fatal_error ("`@catch' parameter is not a known Objective-C class type");
! /* Examine previous @catch clauses for the current @try block for
! superclasses of the 'var_type' class. */
! for (prev_catch = objc_catch_type; TREE_VALUE (prev_catch);
! prev_catch = TREE_CHAIN (prev_catch))
{
! if (TREE_VALUE (prev_catch) == TREE_TYPE (id_type))
! {
! warning ("Exception already handled by preceding `@catch(id)'");
! break;
! }
! else if (!catch_id
! && objc_comptypes (TREE_VALUE (prev_catch), var_type, 0) == 1)
! warning ("Exception of type `%s *' already handled by `@catch (%s *)'",
! IDENTIFIER_POINTER (OBJC_TYPE_NAME (var_type)),
! IDENTIFIER_POINTER (OBJC_TYPE_NAME (TREE_VALUE (prev_catch))));
}
! objc_catch_type = tree_cons (NULL_TREE, var_type, objc_catch_type);
! c_finish_then (objc_exit_block ());
! c_begin_else (0);
! catch_count_stack->val++;
! c_begin_if_stmt ();
! if_nesting_count++;
! if (catch_id)
! cond = integer_one_node;
! else
! {
! cond = get_class_reference (OBJC_TYPE_NAME (var_type));
!
! func_params
! = tree_cons (NULL_TREE, cond,
! tree_cons (NULL_TREE,
! TREE_VALUE (objc_caught_exception),
! NULL_TREE));
! assemble_external (objc_exception_match_decl);
! cond = build_function_call (objc_exception_match_decl, func_params);
! }
! c_finish_if_cond (cond, 0, 0);
! objc_enter_block ();
! objc_declare_variable (RID_REGISTER, var_name,
! build_pointer_type (var_type),
! TREE_VALUE (objc_caught_exception));
}
void
! objc_build_catch_epilogue (void)
{
! /* } else {
! _rethrowException = _caughtException;
! objc_exception_try_exit(&_stackExceptionData);
! }
! } else {
! _rethrowException = objc_exception_extract(&_stackExceptionData);
! }
! }
! } // end TRY-CATCH scope
! */
!
! c_finish_then (objc_exit_block ());
!
! c_begin_else (0);
! objc_enter_block ();
! c_expand_expr_stmt
! (build_modify_expr
! (TREE_VALUE (objc_rethrow_exception),
! NOP_EXPR,
! TREE_VALUE (objc_caught_exception)));
! objc_build_try_exit_fragment ();
! objc_exit_block ();
! while (catch_count_stack->val--)
! {
! /* FIXME. Need to have the block of each else that was opened. */
! c_finish_else ((abort (), NULL)); /* close off all the nested ifs ! */
! c_finish_if_stmt (1);
! if_nesting_count--;
! }
! val_stack_pop (&catch_count_stack);
! objc_caught_exception = TREE_CHAIN (objc_caught_exception);
!
! objc_build_extract_fragment ();
!
! c_finish_else (objc_exit_block ());
! c_finish_if_stmt (1);
! if_nesting_count--;
! objc_exit_block ();
! /* Return to enclosing chain of @catch statements (if any). */
! while (TREE_VALUE (objc_catch_type))
! objc_catch_type = TREE_CHAIN (objc_catch_type);
! objc_catch_type = TREE_PURPOSE (objc_catch_type);
}
! tree
! objc_build_finally_prologue (void)
{
! /* { // begin FINALLY scope
! if (!_rethrowException) {
! objc_exception_try_exit(&_stackExceptionData);
! } */
!
! tree blk = objc_enter_block ();
! c_begin_if_stmt ();
! if_nesting_count++;
! c_finish_if_cond (build_unary_op (TRUTH_NOT_EXPR,
! TREE_VALUE (objc_rethrow_exception), 0),
! 0, 0);
! objc_enter_block ();
! objc_build_try_exit_fragment ();
! c_finish_then (objc_exit_block ());
! c_finish_if_stmt (1);
! if_nesting_count--;
! return blk;
! }
! tree
! objc_build_finally_epilogue (void)
! {
! /* if (_rethrowException) {
! objc_exception_throw(_rethrowException);
}
! } // end FINALLY scope
! } */
! c_begin_if_stmt ();
! if_nesting_count++;
! c_finish_if_cond (TREE_VALUE (objc_rethrow_exception), 0, 0);
! objc_enter_block ();
! objc_build_throw_stmt (TREE_VALUE (objc_rethrow_exception));
! c_finish_then (objc_exit_block ());
! c_finish_if_stmt (1);
! if_nesting_count--;
! objc_exit_block ();
! objc_rethrow_exception = TREE_CHAIN (objc_rethrow_exception);
! objc_stack_exception_data = TREE_CHAIN (objc_stack_exception_data);
! val_stack_pop (&exc_binding_stack);
! return objc_exit_block ();
}
! tree
! objc_build_try_catch_finally_stmt (int has_catch, int has_finally)
{
! /* NB: The operative assumption here is that TRY_FINALLY_EXPR will
! deal with all exits from 'try_catch_blk' and route them through
! 'finally_blk'. */
! /* ??? This is all crock. What the hell is this trying to do? */
! tree outer_blk = objc_build_finally_epilogue ();
! tree prec_stmt = TREE_CHAIN (TREE_CHAIN (outer_blk));
! tree try_catch_blk = TREE_CHAIN (prec_stmt), try_catch_expr;
! tree finally_blk = TREE_CHAIN (try_catch_blk), finally_expr;
! tree succ_stmt = TREE_CHAIN (finally_blk);
! tree try_finally_stmt, try_finally_expr;
! if (!flag_objc_exceptions)
! fatal_error ("Use `-fobjc-exceptions' to enable Objective-C exception syntax");
! /* It is an error to have a @try block without a @catch and/or @finally
! (even though sensible code can be generated nonetheless). */
! if (!has_catch && !has_finally)
error ("`@try' without `@catch' or `@finally'");
! /* We shall now do something truly disgusting. We shall remove the
! 'try_catch_blk' and 'finally_blk' from the 'outer_blk' statement
! chain, and replace them with a TRY_FINALLY_EXPR statement! If
! this doesn't work, we will have to learn (from Per/gcj) how to
! construct the 'outer_blk' lazily. */
! TREE_CHAIN (try_catch_blk) = TREE_CHAIN (finally_blk) = NULL_TREE;
! try_catch_expr = build1 (STMT_EXPR, void_type_node, try_catch_blk);
! TREE_SIDE_EFFECTS (try_catch_expr) = 1;
! finally_expr = build1 (STMT_EXPR, void_type_node, finally_blk);
! TREE_SIDE_EFFECTS (finally_expr) = 1;
! try_finally_expr = build (TRY_FINALLY_EXPR, void_type_node, try_catch_expr,
! finally_expr);
! TREE_SIDE_EFFECTS (try_finally_expr) = 1;
! try_finally_stmt = build_stmt (EXPR_STMT, try_finally_expr);
! TREE_CHAIN (prec_stmt) = try_finally_stmt;
! TREE_CHAIN (try_finally_stmt) = succ_stmt;
!
! return outer_blk; /* the whole enchilada */
}
void
! objc_build_synchronized_prologue (tree sync_expr)
{
- /* {
- id _eval_once = <sync_expr>;
- @try {
- objc_sync_enter( _eval_once ); */
-
tree func_params;
! if (!flag_objc_exceptions)
! fatal_error ("Use `-fobjc-exceptions' to enable Objective-C exception syntax");
! objc_enter_block ();
! objc_eval_once
! = tree_cons (NULL_TREE,
! objc_declare_variable (RID_AUTO,
! get_identifier (UTAG_EVALONCE_VAR),
! id_type,
! sync_expr),
! objc_eval_once);
! objc_build_try_prologue ();
! objc_enter_block ();
! func_params = tree_cons (NULL_TREE,
! TREE_VALUE (objc_eval_once),
! NULL_TREE);
! assemble_external (objc_sync_enter_decl);
! c_expand_expr_stmt (build_function_call
! (objc_sync_enter_decl, func_params));
}
! tree
! objc_build_synchronized_epilogue (void)
{
! /* }
! @finally {
! objc_sync_exit( _eval_once );
! }
! } */
!
! tree func_params;
! objc_exit_block ();
! objc_build_try_epilogue (0);
! objc_build_finally_prologue ();
! func_params = tree_cons (NULL_TREE, TREE_VALUE (objc_eval_once),
! NULL_TREE);
! assemble_external (objc_sync_exit_decl);
! c_expand_expr_stmt (build_function_call (objc_sync_exit_decl,
! func_params));
! objc_build_try_catch_finally_stmt (0, 1);
! return objc_exit_block ();
}
/* Predefine the following data type:
struct _objc_exception_data
--- 2674,3234 ----
}
static tree
! objc_create_temporary_var (tree type)
{
! tree decl;
!
! decl = build_decl (VAR_DECL, NULL_TREE, type);
! TREE_USED (decl) = 1;
! DECL_ARTIFICIAL (decl) = 1;
! DECL_IGNORED_P (decl) = 1;
! DECL_CONTEXT (decl) = current_function_decl;
! return decl;
}
+
+ /* Exception handling constructs. We begin by having the parser do most
+ of the work and passing us blocks. What we do next depends on whether
+ we're doing "native" exception handling or legacy Darwin setjmp exceptions.
+ We abstract all of this in a handful of appropriately named routines. */
! /* Stack of open try blocks. */
!
! struct objc_try_context
{
! struct objc_try_context *outer;
! /* Statements (or statement lists) as processed by the parser. */
! tree try_body;
! tree finally_body;
! /* Some file position locations. */
! location_t try_locus;
! location_t end_try_locus;
! location_t end_catch_locus;
! location_t finally_locus;
! location_t end_finally_locus;
!
! /* A STATEMENT_LIST of CATCH_EXPRs, appropriate for sticking into op1
! of a TRY_CATCH_EXPR. Even when doing Darwin setjmp. */
! tree catch_list;
!
! /* The CATCH_EXPR of an open @catch clause. */
! tree current_catch;
!
! /* The VAR_DECL holding the Darwin equivalent of EXC_PTR_EXPR. */
! tree caught_decl;
! tree stack_decl;
! tree rethrow_decl;
! };
!
! static struct objc_try_context *cur_try_context;
!
! /* This hook, called via lang_eh_runtime_type, generates a runtime object
! that represents TYPE. For Objective-C, this is just the class name. */
! /* ??? Isn't there a class object or some such? Is it easy to get? */
static tree
! objc_eh_runtime_type (tree type)
{
! return add_objc_string (OBJC_TYPE_NAME (TREE_TYPE (type)), class_names);
}
! /* Initialize exception handling. */
!
! static void
! objc_init_exceptions (void)
{
! static bool done = false;
! if (done)
! return;
! done = true;
+ /* Why? */
if (!flag_objc_exceptions)
! warning ("use %<-fobjc-exceptions%> to enable Objective-C "
! "exception syntax");
! if (!flag_objc_sjlj_exceptions)
{
! c_eh_initialized_p = true;
! eh_personality_libfunc
! = init_one_libfunc (USING_SJLJ_EXCEPTIONS
! ? "__gnu_objc_personality_sj0"
! : "__gnu_objc_personality_v0");
! using_eh_for_cleanups ();
! lang_eh_runtime_type = objc_eh_runtime_type;
}
}
! /* Build an EXC_PTR_EXPR, or the moral equivalent. In the case of Darwin,
! we'll arrange for it to be initialized (and associated with a binding)
! later. */
!
! static tree
! objc_build_exc_ptr (void)
{
! if (flag_objc_sjlj_exceptions)
! {
! tree var = cur_try_context->caught_decl;
! if (!var)
! {
! var = objc_create_temporary_var (id_type);
! cur_try_context->caught_decl = var;
! }
! return var;
! }
! else
! return build (EXC_PTR_EXPR, id_type);
}
! /* Build "objc_exception_try_exit(&_stack)". */
!
! static tree
! next_sjlj_build_try_exit (void)
{
! tree t;
! t = build_fold_addr_expr (cur_try_context->stack_decl);
! t = tree_cons (NULL, t, NULL);
! t = build_function_call (objc_exception_try_exit_decl, t);
! return t;
}
! /* Build
! objc_exception_try_enter (&_stack);
! if (_setjmp(&_stack.buf))
! ;
! else
! ;
! Return the COND_EXPR. Note that the THEN and ELSE fields are left
! empty, ready for the caller to fill them in. */
! static tree
! next_sjlj_build_enter_and_setjmp (void)
! {
! tree t, enter, sj, cond;
! t = build_fold_addr_expr (cur_try_context->stack_decl);
! t = tree_cons (NULL, t, NULL);
! enter = build_function_call (objc_exception_try_enter_decl, t);
! t = build_component_ref (cur_try_context->stack_decl,
! get_identifier ("buf"));
! t = build_fold_addr_expr (t);
! t = convert (ptr_type_node, t);
! t = tree_cons (NULL, t, NULL);
! sj = build_function_call (objc_setjmp_decl, t);
! cond = build (COMPOUND_EXPR, TREE_TYPE (sj), enter, sj);
! cond = lang_hooks.truthvalue_conversion (cond);
! return build (COND_EXPR, void_type_node, cond, NULL, NULL);
}
+ /* Build
+ DECL = objc_exception_extract(&_stack);
+ */
+
static tree
! next_sjlj_build_exc_extract (tree decl)
{
! tree t;
! t = build_fold_addr_expr (cur_try_context->stack_decl);
! t = tree_cons (NULL, t, NULL);
! t = build_function_call (objc_exception_extract_decl, t);
! t = convert (TREE_TYPE (decl), t);
! t = build (MODIFY_EXPR, void_type_node, decl, t);
! return t;
}
! /* Build
! if (objc_exception_match(obj_get_class(TYPE), _caught)
! BODY
! else if (...)
! ...
! else
! {
! _rethrow = _caught;
! objc_exception_try_exit(&_stack);
! }
! from the sequence of CATCH_EXPRs in the current try context. */
! static tree
! next_sjlj_build_catch_list (void)
{
! tree_stmt_iterator i = tsi_start (cur_try_context->catch_list);
! tree catch_seq, t;
! tree *last = &catch_seq;
! bool saw_id = false;
! for (; !tsi_end_p (i); tsi_next (&i))
! {
! tree stmt = tsi_stmt (i);
! tree type = CATCH_TYPES (stmt);
! tree body = CATCH_BODY (stmt);
! if (type == NULL)
! {
! *last = body;
! saw_id = true;
! break;
! }
! else
! {
! tree args, cond;
! if (type == error_mark_node)
! cond = error_mark_node;
! else
! {
! args = tree_cons (NULL, cur_try_context->caught_decl, NULL);
! t = get_class_reference (OBJC_TYPE_NAME (TREE_TYPE (type)));
! args = tree_cons (NULL, t, args);
! t = build_function_call (objc_exception_match_decl, args);
! cond = lang_hooks.truthvalue_conversion (t);
! }
! t = build (COND_EXPR, void_type_node, cond, body, NULL);
! SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt));
! *last = t;
! last = &COND_EXPR_ELSE (t);
! }
! }
! if (!saw_id)
! {
! t = build (MODIFY_EXPR, void_type_node, cur_try_context->rethrow_decl,
! cur_try_context->caught_decl);
! annotate_with_locus (t, cur_try_context->end_catch_locus);
! append_to_statement_list (t, last);
! t = next_sjlj_build_try_exit ();
! annotate_with_locus (t, cur_try_context->end_catch_locus);
! append_to_statement_list (t, last);
! }
! return catch_seq;
}
! /* Build a complete @try-@catch-@finally block for legacy Darwin setjmp
! exception handling. We aim to build:
! {
! struct _objc_exception_data _stack;
! id volatile _rethrow = 0;
! try
! {
! objc_exception_try_enter (&_stack);
! if (_setjmp(&_stack.buf))
! {
! id _caught = objc_exception_extract(&_stack);
! objc_exception_try_enter (&_stack);
! if (_setjmp(&_stack.buf))
! _rethrow = objc_exception_extract(&_stack);
! else
! CATCH-LIST
! }
! else
! TRY-BLOCK
! }
! finally
! {
! if (!_rethrow)
! objc_exception_try_exit(&_stack);
! FINALLY-BLOCK
! if (_rethrow)
! objc_exception_throw(_rethrow);
! }
! }
! If CATCH-LIST is empty, we can omit all of the block containing
! "_caught" except for the setting of _rethrow. Note the use of
! a real TRY_FINALLY_EXPR here, which is not involved in EH per-se,
! but handles goto and other exits from the block. */
! static tree
! next_sjlj_build_try_catch_finally (void)
{
! tree rethrow_decl, stack_decl, t;
! tree catch_seq, try_fin, bind;
! /* Create the declarations involved. */
! t = xref_tag (RECORD_TYPE, get_identifier (UTAG_EXCDATA));
! stack_decl = objc_create_temporary_var (t);
! cur_try_context->stack_decl = stack_decl;
! rethrow_decl = objc_create_temporary_var (id_type);
! cur_try_context->rethrow_decl = rethrow_decl;
! TREE_THIS_VOLATILE (rethrow_decl) = 1;
! TREE_CHAIN (rethrow_decl) = stack_decl;
! /* Build the outermost varible binding level. */
! bind = build (BIND_EXPR, void_type_node, rethrow_decl, NULL, NULL);
! annotate_with_locus (bind, cur_try_context->try_locus);
! TREE_SIDE_EFFECTS (bind) = 1;
! /* Initialize rethrow_decl. */
! t = build (MODIFY_EXPR, void_type_node, rethrow_decl,
! convert (id_type, null_pointer_node));
! annotate_with_locus (t, cur_try_context->try_locus);
! append_to_statement_list (t, &BIND_EXPR_BODY (bind));
! /* Build the outermost TRY_FINALLY_EXPR. */
! try_fin = build (TRY_FINALLY_EXPR, void_type_node, NULL, NULL);
! annotate_with_locus (try_fin, cur_try_context->try_locus);
! TREE_SIDE_EFFECTS (try_fin) = 1;
! append_to_statement_list (try_fin, &BIND_EXPR_BODY (bind));
! /* Create the complete catch sequence. */
! if (cur_try_context->catch_list)
{
! tree caught_decl = objc_build_exc_ptr ();
! catch_seq = build_stmt (BIND_EXPR, caught_decl, NULL, NULL);
!
! t = next_sjlj_build_exc_extract (caught_decl);
! append_to_statement_list (t, &BIND_EXPR_BODY (catch_seq));
!
! t = next_sjlj_build_enter_and_setjmp ();
! COND_EXPR_THEN (t) = next_sjlj_build_exc_extract (rethrow_decl);
! COND_EXPR_ELSE (t) = next_sjlj_build_catch_list ();
! append_to_statement_list (t, &BIND_EXPR_BODY (catch_seq));
}
+ else
+ catch_seq = next_sjlj_build_exc_extract (rethrow_decl);
+ annotate_with_locus (catch_seq, cur_try_context->end_try_locus);
! /* Build the main register-and-try if statement. */
! t = next_sjlj_build_enter_and_setjmp ();
! annotate_with_locus (t, cur_try_context->try_locus);
! COND_EXPR_THEN (t) = catch_seq;
! COND_EXPR_ELSE (t) = cur_try_context->try_body;
! TREE_OPERAND (try_fin, 0) = t;
! /* Build the complete FINALLY statement list. */
! t = next_sjlj_build_try_exit ();
! t = build_stmt (COND_EXPR,
! lang_hooks.truthvalue_conversion (rethrow_decl),
! NULL, t);
! annotate_with_locus (t, cur_try_context->finally_locus);
! append_to_statement_list (t, &TREE_OPERAND (try_fin, 1));
! append_to_statement_list (cur_try_context->finally_body,
! &TREE_OPERAND (try_fin, 1));
! t = tree_cons (NULL, rethrow_decl, NULL);
! t = build_function_call (objc_exception_throw_decl, t);
! t = build_stmt (COND_EXPR,
! lang_hooks.truthvalue_conversion (rethrow_decl),
! t, NULL);
! annotate_with_locus (t, cur_try_context->end_finally_locus);
! append_to_statement_list (t, &TREE_OPERAND (try_fin, 1));
! return bind;
}
+ /* Called just after parsing the @try and its associated BODY. We now
+ must prepare for the tricky bits -- handling the catches and finally. */
+
void
! objc_begin_try_stmt (location_t try_locus, tree body)
{
! struct objc_try_context *c = xcalloc (1, sizeof (*c));
! c->outer = cur_try_context;
! c->try_body = body;
! c->try_locus = try_locus;
! c->end_try_locus = input_location;
! cur_try_context = c;
! objc_init_exceptions ();
}
! /* Called just after parsing "@catch (parm)". Open a binding level,
! enter PARM into the binding level, and initialize it. Leave the
! binding level open while the body of the compound statement is parsed. */
!
! void
! objc_begin_catch_clause (tree parm)
{
! tree compound, decl, type, t;
! /* Begin a new scope that the entire catch clause will live in. */
! compound = c_begin_compound_stmt (1);
! /* Turn the raw declarator/declspecs into a decl in the current scope. */
! decl = define_decl (TREE_VALUE (TREE_PURPOSE (parm)),
! TREE_PURPOSE (TREE_PURPOSE (parm)));
! /* Since a decl is required here by syntax, don't warn if its unused. */
! /* ??? As opposed to __attribute__((unused))? Anyway, this appears to
! be what the previous objc implementation did. */
! TREE_USED (decl) = 1;
! /* Verify that the type of the catch is valid. It must be a pointer
! to an Objective-C class, or "id" (which is catch-all). */
! type = TREE_TYPE (decl);
! if (POINTER_TYPE_P (type) && objc_is_object_id (TREE_TYPE (type)))
! type = NULL;
! else if (!POINTER_TYPE_P (type) || !TYPED_OBJECT (TREE_TYPE (type)))
! {
! error ("@catch parameter is not a known Objective-C class type");
! type = error_mark_node;
! }
! else if (cur_try_context->catch_list)
! {
! /* Examine previous @catch clauses and see if we've already
! caught the type in question. */
! tree_stmt_iterator i = tsi_start (cur_try_context->catch_list);
! for (; !tsi_end_p (i); tsi_next (&i))
! {
! tree stmt = tsi_stmt (i);
! t = CATCH_TYPES (stmt);
! if (t == error_mark_node)
! continue;
! if (!t || objc_comptypes (TREE_TYPE (t), TREE_TYPE (type), 0) == 1)
! {
! warning ("exception of type %<%T%> will be caught",
! TREE_TYPE (type));
! warning ("%H by earlier handler for %<%T%>",
! EXPR_LOCUS (stmt), TREE_TYPE (t ? t : id_type));
! break;
! }
}
! }
! /* Record the data for the catch in the try context so that we can
! finalize it later. */
! t = build_stmt (CATCH_EXPR, type, compound);
! cur_try_context->current_catch = t;
! /* Initialize the decl from the EXC_PTR_EXPR we get from the runtime. */
! t = objc_build_exc_ptr ();
! t = convert (TREE_TYPE (decl), t);
! t = build (MODIFY_EXPR, void_type_node, decl, t);
! add_stmt (t);
! }
! /* Called just after parsing the closing brace of a @catch clause. Close
! the open binding level, and record a CATCH_EXPR for it. */
! void
! objc_finish_catch_clause (void)
! {
! tree c = cur_try_context->current_catch;
! cur_try_context->current_catch = NULL;
! cur_try_context->end_catch_locus = input_location;
!
! CATCH_BODY (c) = c_end_compound_stmt (CATCH_BODY (c), 1);
! append_to_statement_list (c, &cur_try_context->catch_list);
}
! /* Called after parsing a @finally clause and its associated BODY.
! Record the body for later placement. */
!
! void
! objc_build_finally_clause (location_t finally_locus, tree body)
{
! cur_try_context->finally_body = body;
! cur_try_context->finally_locus = finally_locus;
! cur_try_context->end_finally_locus = input_location;
! }
! /* Called to finalize a @try construct. */
! void
! objc_finish_try_stmt (void)
! {
! struct objc_try_context *c = cur_try_context;
! tree stmt;
! if (c->catch_list == NULL && c->finally_body == NULL)
error ("`@try' without `@catch' or `@finally'");
! /* If we're doing Darwin setjmp exceptions, build the big nasty. */
! if (flag_objc_sjlj_exceptions)
! {
! if (!cur_try_context->finally_body)
! {
! cur_try_context->finally_locus = input_location;
! cur_try_context->end_finally_locus = input_location;
! }
! stmt = next_sjlj_build_try_catch_finally ();
! }
! else
! {
! /* Otherwise, nest the CATCH inside a FINALLY. */
! stmt = c->try_body;
! if (c->catch_list)
! {
! stmt = build_stmt (TRY_CATCH_EXPR, stmt, c->catch_list);
! annotate_with_locus (stmt, cur_try_context->try_locus);
! }
! if (c->finally_body)
! {
! stmt = build_stmt (TRY_FINALLY_EXPR, stmt, c->finally_body);
! annotate_with_locus (stmt, cur_try_context->try_locus);
! }
! }
! add_stmt (stmt);
! cur_try_context = c->outer;
! free (c);
}
void
! objc_build_throw_stmt (tree throw_expr)
{
tree func_params;
! if (throw_expr == NULL)
! {
! /* If we're not inside a @catch block, there is no "current
! exception" to be rethrown. */
! if (cur_try_context == NULL
! || cur_try_context->current_catch == NULL)
! {
! error ("%<@throw%> (rethrow) used outside of a @catch block");
! return;
! }
! /* Otherwise the object is still sitting in the EXC_PTR_EXPR
! value that we get from the runtime. */
! throw_expr = objc_build_exc_ptr ();
! }
! /* A throw is just a call to the runtime throw function with the
! object as a parameter. */
! func_params = tree_cons (NULL, throw_expr, NULL);
! add_stmt (build_function_call (objc_exception_throw_decl, func_params));
!
! objc_init_exceptions ();
}
! void
! objc_build_synchronized (location_t start_locus, tree mutex, tree body)
{
! tree args, call;
! /* First lock the mutex. */
! mutex = save_expr (mutex);
! args = tree_cons (NULL, mutex, NULL);
! call = build_function_call (objc_sync_enter_decl, args);
! annotate_with_locus (call, start_locus);
! add_stmt (call);
! /* Build the mutex unlock. */
! args = tree_cons (NULL, mutex, NULL);
! call = build_function_call (objc_sync_exit_decl, args);
! annotate_with_locus (call, input_location);
! /* Put the that and the body in a TRY_FINALLY. */
! objc_begin_try_stmt (start_locus, body);
! objc_build_finally_clause (input_location, call);
! objc_finish_try_stmt ();
}
+
/* Predefine the following data type:
struct _objc_exception_data
*************** objc_build_synchronized_epilogue (void)
*** 3259,3265 ****
#endif
static void
! build_objc_exception_stuff (void)
{
tree field_decl, field_decl_chain, index, temp_type;
--- 3249,3255 ----
#endif
static void
! build_next_objc_exception_stuff (void)
{
tree field_decl, field_decl_chain, index, temp_type;
*************** build_objc_exception_stuff (void)
*** 3270,3275 ****
--- 3260,3266 ----
write_symbols = NO_DEBUG;
debug_hooks = &do_nothing_debug_hooks;
+
objc_exception_data_template
= start_struct (RECORD_TYPE, get_identifier (UTAG_EXCDATA));
*************** build_objc_exception_stuff (void)
*** 3317,3336 ****
= builtin_function (TAG_EXCEPTIONTRYENTER, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE);
objc_exception_try_exit_decl
= builtin_function (TAG_EXCEPTIONTRYEXIT, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE);
! /* void objc_exception_throw(id) __attribute__((noreturn)); */
! /* void objc_sync_enter(id); */
! /* void objc_sync_exit(id); */
! temp_type = build_function_type (void_type_node,
! tree_cons (NULL_TREE, id_type,
! void_list_node));
! objc_exception_throw_decl
! = builtin_function (TAG_EXCEPTIONTHROW, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE);
! DECL_ATTRIBUTES (objc_exception_throw_decl)
! = tree_cons (get_identifier ("noreturn"), NULL_TREE, NULL_TREE);
! objc_sync_enter_decl
! = builtin_function (TAG_SYNCENTER, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE);
! objc_sync_exit_decl
! = builtin_function (TAG_SYNCEXIT, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE);
/* int objc_exception_match(id, id); */
temp_type = build_function_type (integer_type_node,
tree_cons (NULL_TREE, id_type,
--- 3308,3314 ----
= builtin_function (TAG_EXCEPTIONTRYENTER, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE);
objc_exception_try_exit_decl
= builtin_function (TAG_EXCEPTIONTRYEXIT, temp_type, 0, NOT_BUILT_IN, NULL, NULL_TREE);
!
/* int objc_exception_match(id, id); */
temp_type = build_function_type (integer_type_node,
tree_cons (NULL_TREE, id_type,
*************** build_objc_exception_stuff (void)
*** 3343,3348 ****
--- 3321,3352 ----
debug_hooks = save_hooks;
}
+ static void
+ build_objc_exception_stuff (void)
+ {
+ tree noreturn_list, nothrow_list, temp_type;
+
+ noreturn_list = tree_cons (get_identifier ("noreturn"), NULL, NULL);
+ nothrow_list = tree_cons (get_identifier ("nothrow"), NULL, NULL);
+
+ /* void objc_exception_throw(id) __attribute__((noreturn)); */
+ /* void objc_sync_enter(id); */
+ /* void objc_sync_exit(id); */
+ temp_type = build_function_type (void_type_node,
+ tree_cons (NULL_TREE, id_type,
+ void_list_node));
+ objc_exception_throw_decl
+ = builtin_function (TAG_EXCEPTIONTHROW, temp_type, 0, NOT_BUILT_IN, NULL,
+ noreturn_list);
+ objc_sync_enter_decl
+ = builtin_function (TAG_SYNCENTER, temp_type, 0, NOT_BUILT_IN,
+ NULL, nothrow_list);
+ objc_sync_exit_decl
+ = builtin_function (TAG_SYNCEXIT, temp_type, 0, NOT_BUILT_IN,
+ NULL, nothrow_list);
+ }
+
+
/* struct <classname> {
struct objc_class *isa;
...
Index: gcc/objc/objc-act.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/objc/objc-act.h,v
retrieving revision 1.22
diff -c -p -d -r1.22 objc-act.h
*** gcc/objc/objc-act.h 1 Jun 2004 15:12:18 -0000 1.22
--- gcc/objc/objc-act.h 18 Jun 2004 00:57:03 -0000
*************** void finish_method_def (void);
*** 39,54 ****
tree start_protocol (enum tree_code, tree, tree);
void finish_protocol (tree);
! tree objc_build_throw_stmt (tree);
! tree objc_build_try_catch_finally_stmt (int, int);
! void objc_build_synchronized_prologue (tree);
! tree objc_build_synchronized_epilogue (void);
! tree objc_build_try_prologue (void);
! void objc_build_try_epilogue (int);
! void objc_build_catch_stmt (tree);
! void objc_build_catch_epilogue (void);
! tree objc_build_finally_prologue (void);
! tree objc_build_finally_epilogue (void);
tree is_ivar (tree, tree);
int is_private (tree);
--- 39,51 ----
tree start_protocol (enum tree_code, tree, tree);
void finish_protocol (tree);
! void objc_build_throw_stmt (tree);
! void objc_begin_try_stmt (location_t, tree);
! void objc_begin_catch_clause (tree);
! void objc_finish_catch_clause (void);
! void objc_build_finally_clause (location_t, tree);
! void objc_finish_try_stmt (void);
! void objc_build_synchronized (location_t, tree, tree);
tree is_ivar (tree, tree);
int is_private (tree);
*************** enum objc_tree_index
*** 282,288 ****
OCTI_LOCAL_EXCEPTION_DECL,
OCTI_RETHROW_EXCEPTION_DECL,
OCTI_EVAL_ONCE_DECL,
- OCTI_EXCEPTION_BLK_STACK,
OCTI_CATCH_TYPE,
OCTI_MAX
--- 279,284 ----
*************** extern GTY(()) tree objc_global_trees[OC
*** 402,409 ****
#define objc_caught_exception objc_global_trees[OCTI_LOCAL_EXCEPTION_DECL]
#define objc_rethrow_exception objc_global_trees[OCTI_RETHROW_EXCEPTION_DECL]
#define objc_eval_once objc_global_trees[OCTI_EVAL_ONCE_DECL]
- #define objc_exception_block_stack \
- objc_global_trees[OCTI_EXCEPTION_BLK_STACK]
#define objc_catch_type objc_global_trees[OCTI_CATCH_TYPE]
#define objc_method_template objc_global_trees[OCTI_METH_TEMPL]
--- 398,403 ----
Index: gcc/testsuite/objc.dg/sync-1.m
===================================================================
RCS file: gcc/testsuite/objc.dg/sync-1.m
diff -N gcc/testsuite/objc.dg/sync-1.m
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- gcc/testsuite/objc.dg/sync-1.m 18 Jun 2004 00:57:03 -0000
***************
*** 0 ****
--- 1,12 ----
+ /* Make sure that @synchronized parses. */
+ /* { dg-options "-fnext-runtime -fobjc-exceptions" } */
+ /* { dg-do compile } */
+
+ #include <objc/Object.h>
+
+ void foo(id sem)
+ {
+ @synchronized (sem) {
+ return;
+ }
+ }
Index: gcc/testsuite/objc.dg/try-catch-1.m
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/objc.dg/try-catch-1.m,v
retrieving revision 1.3
diff -c -p -d -r1.3 try-catch-1.m
*** gcc/testsuite/objc.dg/try-catch-1.m 14 Oct 2003 00:38:48 -0000 1.3
--- gcc/testsuite/objc.dg/try-catch-1.m 18 Jun 2004 00:57:03 -0000
***************
*** 1,8 ****
! /* Test if the compiler accepts @throw / @try..@catch..@finally
! syntax. This will only be usable on MacOS X 10.3 and later,
! but may be compiled on all targets. */
/* Developed by Ziemowit Laski <zlaski@apple.com>. */
! /* { dg-options "-fnext-runtime -fobjc-exceptions" } */
/* { dg-do compile } */
#include <objc/Object.h>
--- 1,6 ----
! /* Test if the compiler accepts @throw / @try..@catch..@finally syntax. */
/* Developed by Ziemowit Laski <zlaski@apple.com>. */
! /* { dg-options "-fobjc-exceptions" } */
/* { dg-do compile } */
#include <objc/Object.h>
Index: gcc/testsuite/objc.dg/try-catch-2.m
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/objc.dg/try-catch-2.m,v
retrieving revision 1.3
diff -c -p -d -r1.3 try-catch-2.m
*** gcc/testsuite/objc.dg/try-catch-2.m 16 Oct 2003 19:12:54 -0000 1.3
--- gcc/testsuite/objc.dg/try-catch-2.m 18 Jun 2004 00:57:03 -0000
***************
*** 2,12 ****
all uncaught exceptions. */
/* Developed by Ziemowit Laski <zlaski@apple.com>. */
! /* { dg-options "-fobjc-exceptions -lobjc" } */
! /* { dg-do run { target *-*-darwin* } } */
- #include <objc/objc.h>
- #include <objc/objc-runtime.h>
#include <objc/Object.h>
#include <stdio.h>
--- 2,10 ----
all uncaught exceptions. */
/* Developed by Ziemowit Laski <zlaski@apple.com>. */
! /* { dg-options "-fobjc-exceptions" } */
! /* { dg-do run } */
#include <objc/Object.h>
#include <stdio.h>
*************** void test (Object* sendPort)
*** 72,81 ****
CHECK_IF(!sendPort);
CHECK_IF(!cleanupPorts);
}
- @catch(Object *obj) { /* { dg-warning "Exception already handled by preceding .\\@catch\\(id\\)." } */
- printf ("Exception caught by incorrect handler!\n");
- CHECK_IF(0);
- }
}
int main (void) {
--- 70,75 ----
Index: gcc/testsuite/objc.dg/try-catch-3.m
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/objc.dg/try-catch-3.m,v
retrieving revision 1.3
diff -c -p -d -r1.3 try-catch-3.m
*** gcc/testsuite/objc.dg/try-catch-3.m 14 Oct 2003 00:38:48 -0000 1.3
--- gcc/testsuite/objc.dg/try-catch-3.m 18 Jun 2004 00:57:03 -0000
***************
*** 3,9 ****
/* Author: Ziemowit Laski <zlaski@apple.com> */
/* { dg-do compile } */
! /* { dg-options "-fnext-runtime -fobjc-exceptions" } */
#include <objc/Object.h>
--- 3,9 ----
/* Author: Ziemowit Laski <zlaski@apple.com> */
/* { dg-do compile } */
! /* { dg-options "-fobjc-exceptions" } */
#include <objc/Object.h>
Index: gcc/testsuite/objc.dg/try-catch-4.m
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/objc.dg/try-catch-4.m,v
retrieving revision 1.3
diff -c -p -d -r1.3 try-catch-4.m
*** gcc/testsuite/objc.dg/try-catch-4.m 14 Oct 2003 00:38:48 -0000 1.3
--- gcc/testsuite/objc.dg/try-catch-4.m 18 Jun 2004 00:57:03 -0000
***************
*** 3,9 ****
/* Author: Ziemowit Laski <zlaski@apple.com> */
/* { dg-do compile } */
! /* { dg-options "-Wall -fnext-runtime -fobjc-exceptions" } */
@interface Exception
@end
--- 3,9 ----
/* Author: Ziemowit Laski <zlaski@apple.com> */
/* { dg-do compile } */
! /* { dg-options "-Wall -fobjc-exceptions" } */
@interface Exception
@end
Index: gcc/testsuite/objc.dg/try-catch-5.m
===================================================================
RCS file: gcc/testsuite/objc.dg/try-catch-5.m
diff -N gcc/testsuite/objc.dg/try-catch-5.m
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- gcc/testsuite/objc.dg/try-catch-5.m 18 Jun 2004 00:57:03 -0000
***************
*** 0 ****
--- 1,27 ----
+ /* Check that the compiler does correctly complain about
+ exceptions being caught by previous @catch blocks. */
+ /* Force the use of NeXT runtime to see that we don't ICE after
+ generating the warning message. */
+
+ /* { dg-do compile } */
+ /* { dg-options "-Wall -fnext-runtime -fobjc-exceptions" } */
+
+ @interface Exception
+ @end
+
+ @interface FooException : Exception
+ @end
+
+ extern void foo();
+
+ void test()
+ {
+ @try {
+ foo();
+ }
+ @catch (Exception* e) { /* { dg-warning "earlier handler" } */
+ }
+ @catch (FooException* fe) { /* { dg-warning "will be caught" } */
+ }
+ }
+
Index: libobjc/Makefile.in
===================================================================
RCS file: /cvs/gcc/gcc/libobjc/Makefile.in,v
retrieving revision 1.41
diff -c -p -d -r1.41 Makefile.in
*** libobjc/Makefile.in 26 May 2004 01:21:46 -0000 1.41
--- libobjc/Makefile.in 18 Jun 2004 00:57:03 -0000
*************** OBJC_H = hash.h objc-list.h sarray.h obj
*** 145,151 ****
OBJS = archive.lo class.lo encoding.lo gc.lo hash.lo init.lo linking.lo \
misc.lo nil_method.lo NXConstStr.lo Object.lo objects.lo \
Protocol.lo sarray.lo selector.lo sendmsg.lo thr.lo \
! $(OBJC_THREAD_FILE).lo
OBJS_GC = archive_gc.lo class_gc.lo encoding_gc.lo gc_gc.lo hash_gc.lo \
init_gc.lo linking_gc.lo misc_gc.lo nil_method_gc.lo \
--- 145,151 ----
OBJS = archive.lo class.lo encoding.lo gc.lo hash.lo init.lo linking.lo \
misc.lo nil_method.lo NXConstStr.lo Object.lo objects.lo \
Protocol.lo sarray.lo selector.lo sendmsg.lo thr.lo \
! $(OBJC_THREAD_FILE).lo exception.lo
OBJS_GC = archive_gc.lo class_gc.lo encoding_gc.lo gc_gc.lo hash_gc.lo \
init_gc.lo linking_gc.lo misc_gc.lo nil_method_gc.lo \
*************** $(OBJC_THREAD_FILE)_gc.lo: $(OBJC_THREAD
*** 253,258 ****
--- 253,262 ----
$(LIBTOOL_COMPILE) $(CC) -c -o $@ $(ALL_CFLAGS) $(OBJC_GCFLAGS) \
$(INCLUDES) $<
+ exception.lo: exception.c
+ $(LIBTOOL_COMPILE) $(CC) -c -o $@ $(ALL_CFLAGS) $(OBJC_GCFLAGS) \
+ -fexceptions $(INCLUDES) $<
+
doc: info dvi html
libobjc$(libext).la: $(OBJS)
Index: libobjc/exception.c
===================================================================
RCS file: libobjc/exception.c
diff -N libobjc/exception.c
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- libobjc/exception.c 18 Jun 2004 00:57:03 -0000
***************
*** 0 ****
--- 1,367 ----
+ /* The implementation of exception handling primitives for Objective-C.
+ Copyright (C) 2004 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 2, 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 COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+ /* As a special exception, if you link this library with files compiled
+ with GCC to produce an executable, this does not cause the resulting
+ executable to be covered by the GNU General Public License. This
+ exception does not however invalidate any other reasons why the
+ executable file might be covered by the GNU General Public License. */
+
+ #include <stdlib.h>
+ #include "objc/objc-api.h"
+ #include "unwind.h"
+ #include "unwind-pe.h"
+
+
+ /* This is the exception class we report -- "GNUCOBJC". */
+ #define __objc_exception_class \
+ ((((((((_Unwind_Exception_Class) 'G' \
+ << 8 | (_Unwind_Exception_Class) 'N') \
+ << 8 | (_Unwind_Exception_Class) 'U') \
+ << 8 | (_Unwind_Exception_Class) 'C') \
+ << 8 | (_Unwind_Exception_Class) 'O') \
+ << 8 | (_Unwind_Exception_Class) 'B') \
+ << 8 | (_Unwind_Exception_Class) 'J') \
+ << 8 | (_Unwind_Exception_Class) 'C')
+
+ /* This is the object that is passed around by the Objective C runtime
+ to represent the exception in flight. */
+
+ struct ObjcException
+ {
+ /* This bit is needed in order to interact with the unwind runtime. */
+ struct _Unwind_Exception base;
+
+ /* The actual object we want to throw. */
+ id value;
+
+ /* Cache some internal unwind data between phase 1 and phase 2. */
+ _Unwind_Ptr landingPad;
+ int handlerSwitchValue;
+ };
+
+
+
+ struct lsda_header_info
+ {
+ _Unwind_Ptr Start;
+ _Unwind_Ptr LPStart;
+ _Unwind_Ptr ttype_base;
+ const unsigned char *TType;
+ const unsigned char *action_table;
+ unsigned char ttype_encoding;
+ unsigned char call_site_encoding;
+ };
+
+ static const unsigned char *
+ parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p,
+ struct lsda_header_info *info)
+ {
+ _Unwind_Word tmp;
+ unsigned char lpstart_encoding;
+
+ info->Start = (context ? _Unwind_GetRegionStart (context) : 0);
+
+ /* Find @LPStart, the base to which landing pad offsets are relative. */
+ lpstart_encoding = *p++;
+ if (lpstart_encoding != DW_EH_PE_omit)
+ p = read_encoded_value (context, lpstart_encoding, p, &info->LPStart);
+ else
+ info->LPStart = info->Start;
+
+ /* Find @TType, the base of the handler and exception spec type data. */
+ info->ttype_encoding = *p++;
+ if (info->ttype_encoding != DW_EH_PE_omit)
+ {
+ p = read_uleb128 (p, &tmp);
+ info->TType = p + tmp;
+ }
+ else
+ info->TType = 0;
+
+ /* The encoding and length of the call-site table; the action table
+ immediately follows. */
+ info->call_site_encoding = *p++;
+ p = read_uleb128 (p, &tmp);
+ info->action_table = p + tmp;
+
+ return p;
+ }
+
+ static Class
+ get_ttype_entry (struct lsda_header_info *info, _Unwind_Word i)
+ {
+ _Unwind_Ptr ptr;
+
+ i *= size_of_encoded_value (info->ttype_encoding);
+ read_encoded_value_with_base (info->ttype_encoding, info->ttype_base,
+ info->TType - i, &ptr);
+
+ /* NULL ptr means catch-all. */
+ if (ptr)
+ return objc_get_class ((const char *) ptr);
+ else
+ return 0;
+ }
+
+ /* Like unto the method of the same name on Object, but takes an id. */
+ /* ??? Does this bork the meta-type system? Can/should we look up an
+ isKindOf method on the id? */
+
+ static int
+ isKindOf (id value, Class target)
+ {
+ Class c;
+
+ /* NULL target is catch-all. */
+ if (target == 0)
+ return 1;
+
+ for (c = value->class_pointer; c; c = class_get_super_class (c))
+ if (c == target)
+ return 1;
+ return 0;
+ }
+
+ /* Using a different personality function name causes link failures
+ when trying to mix code using different exception handling models. */
+ #ifdef SJLJ_EXCEPTIONS
+ #define PERSONALITY_FUNCTION __gnu_objc_personality_sj0
+ #define __builtin_eh_return_data_regno(x) x
+ #else
+ #define PERSONALITY_FUNCTION __gnu_objc_personality_v0
+ #endif
+
+ _Unwind_Reason_Code
+ PERSONALITY_FUNCTION (int version,
+ _Unwind_Action actions,
+ _Unwind_Exception_Class exception_class,
+ struct _Unwind_Exception *ue_header,
+ struct _Unwind_Context *context)
+ {
+ struct ObjcException *xh = (struct ObjcException *) ue_header;
+
+ struct lsda_header_info info;
+ const unsigned char *language_specific_data;
+ const unsigned char *action_record;
+ const unsigned char *p;
+ _Unwind_Ptr landing_pad, ip;
+ int handler_switch_value;
+ int saw_cleanup, saw_handler;
+
+ /* Interface version check. */
+ if (version != 1)
+ return _URC_FATAL_PHASE1_ERROR;
+
+ /* Shortcut for phase 2 found handler for domestic exception. */
+ if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME)
+ && exception_class == __objc_exception_class)
+ {
+ handler_switch_value = xh->handlerSwitchValue;
+ landing_pad = xh->landingPad;
+ goto install_context;
+ }
+
+ language_specific_data = (const unsigned char *)
+ _Unwind_GetLanguageSpecificData (context);
+
+ /* If no LSDA, then there are no handlers or cleanups. */
+ if (! language_specific_data)
+ return _URC_CONTINUE_UNWIND;
+
+ /* Parse the LSDA header. */
+ p = parse_lsda_header (context, language_specific_data, &info);
+ info.ttype_base = base_of_encoded_value (info.ttype_encoding, context);
+ ip = _Unwind_GetIP (context) - 1;
+ landing_pad = 0;
+ action_record = 0;
+ handler_switch_value = 0;
+
+ #ifdef SJLJ_EXCEPTIONS
+ /* The given "IP" is an index into the call-site table, with two
+ exceptions -- -1 means no-action, and 0 means terminate. But
+ since we're using uleb128 values, we've not got random access
+ to the array. */
+ if ((int) ip < 0)
+ return _URC_CONTINUE_UNWIND;
+ else
+ {
+ _Unwind_Word cs_lp, cs_action;
+ do
+ {
+ p = read_uleb128 (p, &cs_lp);
+ p = read_uleb128 (p, &cs_action);
+ }
+ while (--ip);
+
+ /* Can never have null landing pad for sjlj -- that would have
+ been indicated by a -1 call site index. */
+ landing_pad = cs_lp + 1;
+ if (cs_action)
+ action_record = info.action_table + cs_action - 1;
+ goto found_something;
+ }
+ #else
+ /* Search the call-site table for the action associated with this IP. */
+ while (p < info.action_table)
+ {
+ _Unwind_Ptr cs_start, cs_len, cs_lp;
+ _Unwind_Word cs_action;
+
+ /* Note that all call-site encodings are "absolute" displacements. */
+ p = read_encoded_value (0, info.call_site_encoding, p, &cs_start);
+ p = read_encoded_value (0, info.call_site_encoding, p, &cs_len);
+ p = read_encoded_value (0, info.call_site_encoding, p, &cs_lp);
+ p = read_uleb128 (p, &cs_action);
+
+ /* The table is sorted, so if we've passed the ip, stop. */
+ if (ip < info.Start + cs_start)
+ p = info.action_table;
+ else if (ip < info.Start + cs_start + cs_len)
+ {
+ if (cs_lp)
+ landing_pad = info.LPStart + cs_lp;
+ if (cs_action)
+ action_record = info.action_table + cs_action - 1;
+ goto found_something;
+ }
+ }
+ #endif /* SJLJ_EXCEPTIONS */
+
+ /* If ip is not present in the table, C++ would call terminate. */
+ /* ??? As with Java, it's perhaps better to tweek the LSDA to
+ that no-action is mapped to no-entry. */
+ return _URC_CONTINUE_UNWIND;
+
+ found_something:
+ saw_cleanup = 0;
+ saw_handler = 0;
+
+ if (landing_pad == 0)
+ {
+ /* If ip is present, and has a null landing pad, there are
+ no cleanups or handlers to be run. */
+ }
+ else if (action_record == 0)
+ {
+ /* If ip is present, has a non-null landing pad, and a null
+ action table offset, then there are only cleanups present.
+ Cleanups use a zero switch value, as set above. */
+ saw_cleanup = 1;
+ }
+ else
+ {
+ /* Otherwise we have a catch handler. */
+ _Unwind_Sword ar_filter, ar_disp;
+
+ while (1)
+ {
+ p = action_record;
+ p = read_sleb128 (p, &ar_filter);
+ read_sleb128 (p, &ar_disp);
+
+ if (ar_filter == 0)
+ {
+ /* Zero filter values are cleanups. */
+ saw_cleanup = 1;
+ }
+
+ /* During forced unwinding, we only run cleanups. With a
+ foreign exception class, we have no class info to match. */
+ else if ((actions & _UA_FORCE_UNWIND)
+ || exception_class != __objc_exception_class)
+ ;
+
+ else if (ar_filter > 0)
+ {
+ /* Positive filter values are handlers. */
+
+ Class catch_type = get_ttype_entry (&info, ar_filter);
+
+ if (isKindOf (xh->value, catch_type))
+ {
+ handler_switch_value = ar_filter;
+ saw_handler = 1;
+ break;
+ }
+ }
+ else
+ {
+ /* Negative filter values are exception specifications,
+ which Objective-C does not use. */
+ abort ();
+ }
+
+ if (ar_disp == 0)
+ break;
+ action_record = p + ar_disp;
+ }
+ }
+
+ if (! saw_handler && ! saw_cleanup)
+ return _URC_CONTINUE_UNWIND;
+
+ if (actions & _UA_SEARCH_PHASE)
+ {
+ if (!saw_handler)
+ return _URC_CONTINUE_UNWIND;
+
+ /* For domestic exceptions, we cache data from phase 1 for phase 2. */
+ if (exception_class == __objc_exception_class)
+ {
+ xh->handlerSwitchValue = handler_switch_value;
+ xh->landingPad = landing_pad;
+ }
+ return _URC_HANDLER_FOUND;
+ }
+
+ install_context:
+ _Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
+ __builtin_extend_pointer (xh->value));
+ _Unwind_SetGR (context, __builtin_eh_return_data_regno (1),
+ handler_switch_value);
+ _Unwind_SetIP (context, landing_pad);
+ return _URC_INSTALL_CONTEXT;
+ }
+
+ static void
+ __objc_exception_cleanup (_Unwind_Reason_Code code __attribute__((unused)),
+ struct _Unwind_Exception *exc)
+ {
+ free (exc);
+ }
+
+ void
+ objc_exception_throw (id value)
+ {
+ struct ObjcException *header = calloc (1, sizeof (*header));
+ header->base.exception_class = __objc_exception_class;
+ header->base.exception_cleanup = __objc_exception_cleanup;
+ header->value = value;
+
+ #ifdef _GLIBCXX_SJLJ_EXCEPTIONS
+ _Unwind_SjLj_RaiseException (&header->base);
+ #else
+ _Unwind_RaiseException (&header->base);
+ #endif
+
+ /* Some sort of unwinding error. */
+ abort ();
+ }