Patch: Fix for PR java/4766

Tom Tromey tromey@redhat.com
Sat Nov 17 13:31:00 GMT 2001


This fixes PR java/4766.

The problem here was that if the `finally' clause of a try-finally
couldn't complete normally, we would generate incorrect bytecode.  For
instance, in the example given in the PR (which is now in the libjava
test suite), we would generate a `goto' past the end of the bytecode.

The fix is to notice when a `finally' can't return normally, and in
that case not generate the finally code as a subroutine.  This is what
Sun javac and jikes does.  (I looked at jikes, too, but it also
generates invalid bytecode.)

I rebuilt libgcj with this with no problems.

Ok to commit?

Tom

Index: ChangeLog
from  Tom Tromey  <tromey@redhat.com>
	For PR java/4766:
	* jcf-write.c (generate_bytecode_insns) [TRY_FINALLY_EXPR]: Handle
	case where `finally' clause can't complete normally.

2001-12-20  Tom Tromey  <tromey@redhat.com>

Index: jcf-write.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/jcf-write.c,v
retrieving revision 1.96
diff -u -r1.96 jcf-write.c
--- jcf-write.c 2001/12/16 16:23:49 1.96
+++ jcf-write.c 2001/12/20 17:55:21
@@ -1348,7 +1348,7 @@
     abort ();
 }
 
-/* Call pending cleanups i.e. those for surrounding TRY_FINAL_EXPRs.
+/* Call pending cleanups i.e. those for surrounding TRY_FINALLY_EXPRs.
    but only as far out as LIMIT (since we are about to jump to the
    emit label that is LIMIT). */
 
@@ -1683,8 +1683,8 @@
 	   1.  the switch_expression (the value used to select the correct case);
 	   2.  the switch_body;
 	   3.  the switch_instruction (the tableswitch/loopupswitch instruction.).
-	   After code generation, we will re-order then in the order 1, 3, 2.
-	   This is to avoid an extra GOTOs. */
+	   After code generation, we will re-order them in the order 1, 3, 2.
+	   This is to avoid any extra GOTOs. */
 	struct jcf_switch_state sw_state;
 	struct jcf_block *expression_last; /* Last block of the switch_expression. */
 	struct jcf_block *body_last; /* Last block of the switch_body. */
@@ -2298,7 +2298,8 @@
 	  {
 	    tree catch_clause = TREE_OPERAND (clause, 0);
 	    tree exception_decl = BLOCK_EXPR_DECLS (catch_clause);
-	    struct jcf_handler *handler = alloc_handler (start_label, end_label, state);
+	    struct jcf_handler *handler = alloc_handler (start_label,
+							 end_label, state);
 	    if (exception_decl == NULL_TREE)
 	      handler->type = NULL_TREE;
 	    else
@@ -2314,8 +2315,8 @@
 
     case TRY_FINALLY_EXPR:
       {
-	struct jcf_block *finished_label,
-	  *finally_label, *start_label, *end_label;
+	struct jcf_block *finished_label = NULL;
+	struct jcf_block *finally_label, *start_label, *end_label;
 	struct jcf_handler *handler;
 	tree try_block = TREE_OPERAND (exp, 0);
 	tree finally = TREE_OPERAND (exp, 1);
@@ -2325,15 +2326,26 @@
 
 	finally_label = gen_jcf_label (state);
 	start_label = get_jcf_label_here (state);
-	finally_label->pc = PENDING_CLEANUP_PC;
-	finally_label->next = state->labeled_blocks;
-	state->labeled_blocks = finally_label;
-	state->num_finalizers++;
+	/* If the `finally' clause can complete normally, we emit it
+	   as a subroutine and let the other clauses call it via
+	   `jsr'.  If it can't complete normally, then we simply emit
+	   `goto's directly to it.  */
+	if (CAN_COMPLETE_NORMALLY (finally))
+	  {
+	    finally_label->pc = PENDING_CLEANUP_PC;
+	    finally_label->next = state->labeled_blocks;
+	    state->labeled_blocks = finally_label;
+	    state->num_finalizers++;
+	  }
 
 	generate_bytecode_insns (try_block, target, state);
-	if (state->labeled_blocks != finally_label)
-	  abort();
-	state->labeled_blocks = finally_label->next;
+
+	if (CAN_COMPLETE_NORMALLY (finally))
+	  {
+	    if (state->labeled_blocks != finally_label)
+	      abort();
+	    state->labeled_blocks = finally_label->next;
+	  }
 	end_label = get_jcf_label_here (state);
 
 	if (end_label == start_label)
@@ -2344,43 +2356,75 @@
 	    break;
 	  }
 
-	return_link = build_decl (VAR_DECL, NULL_TREE,
-				  return_address_type_node);
-	finished_label = gen_jcf_label (state);
-
+	if (CAN_COMPLETE_NORMALLY (finally))
+	  {
+	    return_link = build_decl (VAR_DECL, NULL_TREE,
+				      return_address_type_node);
+	    finished_label = gen_jcf_label (state);
+	  }
 
 	if (CAN_COMPLETE_NORMALLY (try_block))
 	  {
-	    emit_jsr (finally_label, state);
-	    emit_goto (finished_label, state);
+	    if (CAN_COMPLETE_NORMALLY (finally))
+	      {
+		emit_jsr (finally_label, state);
+		emit_goto (finished_label, state);
+	      }
+	    else
+	      emit_goto (finally_label, state);
 	  }
 
-	/* Handle exceptions. */
+	/* Handle exceptions.  */
 
 	exception_type = build_pointer_type (throwable_type_node);
-	exception_decl = build_decl (VAR_DECL, NULL_TREE, exception_type);
-	localvar_alloc (return_link, state);
+	if (CAN_COMPLETE_NORMALLY (finally))
+	  {
+	    /* We're going to generate a subroutine, so we'll need to
+	       save and restore the exception around the `jsr'.  */ 
+	    exception_decl = build_decl (VAR_DECL, NULL_TREE, exception_type);
+	    localvar_alloc (return_link, state);
+	  }
 	handler = alloc_handler (start_label, end_label, state);
 	handler->type = NULL_TREE;
-	localvar_alloc (exception_decl, state);
-	NOTE_PUSH (1);
-	emit_store (exception_decl, state);
-	emit_jsr (finally_label, state);
-	emit_load (exception_decl, state);
-	RESERVE (1);
-	OP1 (OPCODE_athrow);
-	NOTE_POP (1);
+	if (CAN_COMPLETE_NORMALLY (finally))
+	  {
+	    localvar_alloc (exception_decl, state);
+	    NOTE_PUSH (1);
+	    emit_store (exception_decl, state);
+	    emit_jsr (finally_label, state);
+	    emit_load (exception_decl, state);
+	    RESERVE (1);
+	    OP1 (OPCODE_athrow);
+	    NOTE_POP (1);
+	  }
+	else
+	  {
+	    /* We're not generating a subroutine.  In this case we can
+	       simply have the exception handler pop the exception and
+	       then fall through to the `finally' block.  */
+	    NOTE_PUSH (1);
+	    emit_pop (1, state);
+	    NOTE_POP (1);
+	  }
 
-	/* The finally block.  First save return PC into return_link. */
+	/* The finally block.  If we're generating a subroutine, first
+	   save return PC into return_link.  Otherwise, just generate
+	   the code for the `finally' block.  */
 	define_jcf_label (finally_label, state);
-	NOTE_PUSH (1);
-	emit_store (return_link, state);
+	if (CAN_COMPLETE_NORMALLY (finally))
+	  {
+	    NOTE_PUSH (1);
+	    emit_store (return_link, state);
+	  }
 
 	generate_bytecode_insns (finally, IGNORE_TARGET, state);
-	maybe_wide (OPCODE_ret, DECL_LOCAL_INDEX (return_link), state);
-	localvar_free (exception_decl, state);
-	localvar_free (return_link, state);
-	define_jcf_label (finished_label, state);
+	if (CAN_COMPLETE_NORMALLY (finally))
+	  {
+	    maybe_wide (OPCODE_ret, DECL_LOCAL_INDEX (return_link), state);
+	    localvar_free (exception_decl, state);
+	    localvar_free (return_link, state);
+	    define_jcf_label (finished_label, state);
+	  }
       }
       break;
     case THROW_EXPR:



More information about the Java-patches mailing list