OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / java / jcf-write.c
index 64e4905..2988c47 100644 (file)
@@ -1,5 +1,5 @@
 /* Write out a Java(TM) class file.
-   Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -25,6 +25,7 @@ The Free Software Foundation is independent of Sun Microsystems, Inc.  */
 #include "system.h"
 #include "jcf.h"
 #include "tree.h"
+#include "real.h"
 #include "java-tree.h"
 #include "obstack.h"
 #undef AND
@@ -34,6 +35,7 @@ The Free Software Foundation is independent of Sun Microsystems, Inc.  */
 #include "parse.h" /* for BLOCK_EXPR_BODY */
 #include "buffer.h"
 #include "toplev.h"
+#include "ggc.h"
 
 #ifndef DIR_SEPARATOR
 #define DIR_SEPARATOR '/'
@@ -104,14 +106,14 @@ struct chunk
    to the beginning of the block.
 
    If (pc < 0), the jcf_block is not an actual block (i.e. it has no
-   assocated code yet), but it is an undefined label.
+   associated code yet), but it is an undefined label.
 */
 
 struct jcf_block
 {
   /* For blocks that that are defined, the next block (in pc order).
-     For blocks that are the not-yet-defined end label of a LABELED_BLOCK_EXPR
-     or a cleanup expression (from a WITH_CLEANUP_EXPR),
+     For blocks that are not-yet-defined the end label of a LABELED_BLOCK_EXPR
+     or a cleanup expression (from a TRY_FINALLY_EXPR),
      this is the next (outer) such end label, in a stack headed by
      labeled_blocks in jcf_partial. */
   struct jcf_block *next;
@@ -130,13 +132,14 @@ struct jcf_block
 
   int linenumber;
 
-  /* After finish_jcf_block is called, The actual instructions contained in this block.
-     Before than NULL, and the instructions are in state->bytecode. */
+  /* After finish_jcf_block is called, the actual instructions
+     contained in this block.  Before that NULL, and the instructions
+     are in state->bytecode. */
   union {
     struct chunk *chunk;
 
     /* If pc==PENDING_CLEANUP_PC, start_label is the start of the region
-       coveed by the cleanup. */
+       covered by the cleanup. */
     struct jcf_block *start_label;
   } v;
 
@@ -271,7 +274,7 @@ struct jcf_partial
   /* If non-NULL, use this for the return value. */
   tree return_value_decl;
 
-  /* Information about the current switch statemenet. */
+  /* Information about the current switch statement. */
   struct jcf_switch_state *sw_state;
 };
 
@@ -342,21 +345,25 @@ static char *make_class_file_name PARAMS ((tree));
 static unsigned char *append_synthetic_attribute PARAMS ((struct jcf_partial *));
 static void append_innerclasses_attribute PARAMS ((struct jcf_partial *, tree));
 static void append_innerclasses_attribute_entry PARAMS ((struct jcf_partial *, tree, tree));
+static void append_gcj_attribute PARAMS ((struct jcf_partial *, tree));
 
 /* Utility macros for appending (big-endian) data to a buffer.
    We assume a local variable 'ptr' points into where we want to
-   write next, and we assume enoygh space has been allocated. */
+   write next, and we assume enough space has been allocated. */
 
-#ifdef ENABLE_CHECKING
-int
-CHECK_PUT(ptr, state, i)
+#ifdef ENABLE_JC1_CHECKING
+static int CHECK_PUT PARAMS ((void *, struct jcf_partial *, int));
+
+static int
+CHECK_PUT (ptr, state, i)
      void *ptr;
      struct jcf_partial *state;
      int i;
 {
-  if (ptr < state->chunk->data
-      || (char*)ptr + i > state->chunk->data + state->chunk->size)
-    fatal ("internal error - CHECK_PUT failed");
+  if ((unsigned char *) ptr < state->chunk->data
+      || (unsigned char *) ptr + i > state->chunk->data + state->chunk->size)
+    abort ();
+
   return 0;
 }
 #else
@@ -401,18 +408,20 @@ alloc_chunk (last, data, size, work)
   return chunk;
 }
 
-#ifdef ENABLE_CHECKING
-int
-CHECK_OP(struct jcf_partial *state)
+#ifdef ENABLE_JC1_CHECKING
+static int CHECK_OP PARAMS ((struct jcf_partial *));
+
+static int
+CHECK_OP (state)
+     struct jcf_partial *state;
 {
   if (state->bytecode.ptr > state->bytecode.limit)
-    {
-      fatal("internal error - CHECK_OP failed");
-    }
+    abort ();
+
   return 0;
 }
 #else
-#define CHECK_OP(STATE) ((void)0)
+#define CHECK_OP(STATE) ((void) 0)
 #endif
 
 static unsigned char *
@@ -677,9 +686,12 @@ get_access_flags (decl)
       if (ANONYMOUS_CLASS_P (TREE_TYPE (decl))
          || LOCAL_CLASS_P (TREE_TYPE (decl)))
        flags |= ACC_PRIVATE;
+      if (CLASS_STRICTFP (decl))
+       flags |= ACC_STRICT;
     }
   else
-    fatal ("internal error - bad argument to get_access_flags");
+    abort ();
+
   if (TREE_CODE (decl) == FUNCTION_DECL)
     {
       if (METHOD_NATIVE (decl))
@@ -690,6 +702,8 @@ get_access_flags (decl)
        flags |= ACC_SYNCHRONIZED;
       if (METHOD_ABSTRACT (decl))
        flags |= ACC_ABSTRACT;
+      if (METHOD_STRICTFP (decl))
+       flags |= ACC_STRICT;
     }
   if (isfield)
     {
@@ -825,11 +839,10 @@ find_constant_index (value, state)
        }
     }
   else if (TREE_CODE (value) == STRING_CST)
-    {
-      return find_string_constant (&state->cpool, value);
-    }
+    return find_string_constant (&state->cpool, value);
+
   else
-    fatal ("find_constant_index - bad type");
+    abort ();
 }
 
 /* Push 64-bit long constant on VM stack.
@@ -840,15 +853,20 @@ push_long_const (lo, hi, state)
      HOST_WIDE_INT lo, hi;
      struct jcf_partial *state;
 {
-  if (hi == 0 && lo >= 0 && lo <= 1)
+  HOST_WIDE_INT highpart, dummy;
+  jint lowpart = WORD_TO_INT (lo);
+
+  rshift_double (lo, hi, 32, 64, &highpart, &dummy, 1);
+
+  if (highpart == 0 && (lowpart == 0 || lowpart == 1))
     {
       RESERVE(1);
-      OP1(OPCODE_lconst_0 + lo);
+      OP1(OPCODE_lconst_0 + lowpart);
     }
-  else if ((hi == 0 && (jword)(lo  & 0xFFFFFFFF) < 32768) 
-          || (hi == -1 && (jword)(lo & 0xFFFFFFFF) >= -32768))
+  else if ((highpart == 0 && lowpart > 0 && lowpart < 32768) 
+          || (highpart == -1 && lowpart < 0 && lowpart >= -32768))
       {
-        push_int_const (lo, state);
+        push_int_const (lowpart, state);
         RESERVE (1);
         OP1 (OPCODE_i2l);
       }
@@ -1168,12 +1186,12 @@ generate_bytecode_conditional (exp, true_label, false_label,
                                       true_label, false_label,
                                       true_branch_first, state);
        if (state->code_SP != save_SP_after)
-         fatal ("internal error  non-matching SP");
+         abort ();
       }
       break;
     case TRUTH_NOT_EXPR:
-      generate_bytecode_conditional (TREE_OPERAND (exp, 0), false_label, true_label,
-                                    ! true_branch_first, state);
+      generate_bytecode_conditional (TREE_OPERAND (exp, 0), false_label,
+                                    true_label, ! true_branch_first, state);
       break;
     case TRUTH_ANDIF_EXPR:
       {
@@ -1250,7 +1268,7 @@ generate_bytecode_conditional (exp, true_label, false_label,
            }
          if (integer_zerop (exp1) || integer_zerop (exp0))
            {
-             generate_bytecode_insns (integer_zerop (exp1) ? exp0 : exp0,
+             generate_bytecode_insns (integer_zerop (exp0) ? exp1 : exp0,
                                       STACK_TARGET, state);
              op = op + (OPCODE_ifnull - OPCODE_if_acmpeq);
              negop = (op & 1) ? op - 1 : op + 1;
@@ -1337,10 +1355,10 @@ generate_bytecode_conditional (exp, true_label, false_label,
       break;
     }
   if (save_SP != state->code_SP)
-    fatal ("internal error - SP mismatch");
+    abort ();
 }
 
-/* Call pending cleanups i.e. those for surrounding CLEANUP_POINT_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). */
 
@@ -1396,7 +1414,7 @@ generate_bytecode_return (exp, state)
   if (returns_void)
     {
       op = OPCODE_return;
-      call_cleanups (NULL_PTR, state);
+      call_cleanups (NULL, state);
     }
   else
     {
@@ -1410,7 +1428,7 @@ generate_bytecode_return (exp, state)
              localvar_alloc (state->return_value_decl, state);
            }
          emit_store (state->return_value_decl, state);
-         call_cleanups (NULL_PTR, state);
+         call_cleanups (NULL, state);
          emit_load (state->return_value_decl, state);
          /* If we call localvar_free (state->return_value_decl, state),
             then we risk the save decl erroneously re-used in the
@@ -1432,7 +1450,7 @@ generate_bytecode_insns (exp, target, state)
      int target;
      struct jcf_partial *state;
 {
-  tree type;
+  tree type, arg;
   enum java_opcode jopcode;
   int op;
   HOST_WIDE_INT value;
@@ -1473,9 +1491,13 @@ generate_bytecode_insns (exp, target, state)
            }
        }
       break;
-      case COMPOUND_EXPR:      
+    case COMPOUND_EXPR:        
       generate_bytecode_insns (TREE_OPERAND (exp, 0), IGNORE_TARGET, state);
-      generate_bytecode_insns (TREE_OPERAND (exp, 1), target, state);
+      /* Normally the first operand to a COMPOUND_EXPR must complete
+        normally.  However, in the special case of a do-while
+        statement this is not necessarily the case.  */
+      if (CAN_COMPLETE_NORMALLY (TREE_OPERAND (exp, 0)))
+       generate_bytecode_insns (TREE_OPERAND (exp, 1), target, state);
       break;
     case EXPR_WITH_FILE_LOCATION:
       {
@@ -1520,7 +1542,7 @@ generate_bytecode_insns (exp, target, state)
       {
        int prec = TYPE_PRECISION (type) >> 5;
        RESERVE(1);
-       if (real_zerop (exp))
+       if (real_zerop (exp) && ! REAL_VALUE_MINUS_ZERO (TREE_REAL_CST (exp)))
          OP1 (prec == 1 ? OPCODE_fconst_0 : OPCODE_dconst_0);
        else if (real_onep (exp))
          OP1 (prec == 1 ? OPCODE_fconst_1 : OPCODE_dconst_1);
@@ -1675,8 +1697,8 @@ generate_bytecode_insns (exp, target, state)
           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. */
@@ -1691,7 +1713,9 @@ generate_bytecode_insns (exp, target, state)
        sw_state.default_label = NULL;
        generate_bytecode_insns (TREE_OPERAND (exp, 0), STACK_TARGET, state);
        expression_last = state->last_block;
-       body_block = get_jcf_label_here (state);  /* Force a new block here. */
+       /* Force a new block here.  */
+       body_block = gen_jcf_label (state);
+       define_jcf_label (body_block, state);
        generate_bytecode_insns (TREE_OPERAND (exp, 1), IGNORE_TARGET, state);
        body_last = state->last_block;
 
@@ -1710,8 +1734,9 @@ generate_bytecode_insns (exp, target, state)
            else
              {
                push_int_const (sw_state.cases->offset, state);
+               NOTE_PUSH (1);
                emit_if (sw_state.cases->label,
-                        OPCODE_ifeq, OPCODE_ifne, state);
+                        OPCODE_if_icmpeq, OPCODE_if_icmpne, state);
              }
            emit_goto (sw_state.default_label, state);
          }
@@ -1746,7 +1771,8 @@ generate_bytecode_insns (exp, target, state)
                    gap_start--;
                  }
                relocs[gap_start++] = reloc;
-               /* Note we don't check for duplicates.  FIXME! */
+               /* Note we don't check for duplicates.  This is
+                  handled by the parser.  */
              }
 
            if (2 * sw_state.num_cases
@@ -1868,7 +1894,8 @@ generate_bytecode_insns (exp, target, state)
          {
            struct jcf_block *head_label = get_jcf_label_here (state);
            generate_bytecode_insns (body, IGNORE_TARGET, state);
-           emit_goto (head_label, state);
+           if (CAN_COMPLETE_NORMALLY (body))
+             emit_goto (head_label, state);
          }
       }
       break;
@@ -1898,6 +1925,7 @@ generate_bytecode_insns (exp, target, state)
     case POSTINCREMENT_EXPR: value =  1; post_op = 1;  goto increment;
     increment:
 
+      arg = TREE_OPERAND (exp, 1);
       exp = TREE_OPERAND (exp, 0);
       type = TREE_TYPE (exp);
       size = TYPE_IS_WIDE (type) ? 2 : 1;
@@ -1950,12 +1978,10 @@ generate_bytecode_insns (exp, target, state)
       /* Stack, if ARRAY_REF:  ..., [result, ] array, index, oldvalue. */
       /* Stack, if COMPONENT_REF:  ..., [result, ] objectref, oldvalue. */
       /* Stack, otherwise:  ..., [result, ] oldvalue. */
-      if (size == 1)
-       push_int_const (value, state);
-      else
-       push_long_const (value, (HOST_WIDE_INT)(value >= 0 ? 0 : -1), state);
-      NOTE_PUSH (size);
-      emit_binop (OPCODE_iadd + adjust_typed_op (type, 3), type, state);
+      generate_bytecode_insns (arg, STACK_TARGET, state);
+      emit_binop ((value >= 0 ? OPCODE_iadd : OPCODE_isub)
+                 + adjust_typed_op (type, 3),
+                 type, state);
       if (target != IGNORE_TARGET && ! post_op)
        emit_dup (size, offset, state);
       /* Stack, if ARRAY_REF:  ..., [result, ] array, index, newvalue. */
@@ -2000,6 +2026,8 @@ generate_bytecode_insns (exp, target, state)
                    if (TREE_CODE (rhs) == MINUS_EXPR)
                      value = -value;
                    emit_iinc (lhs, value, state);
+                   if (target != IGNORE_TARGET)
+                     emit_load (lhs, state);
                    break;
                  }
              }
@@ -2021,6 +2049,61 @@ generate_bytecode_insns (exp, target, state)
          }
        else
          offset = 0;
+
+       /* If the rhs is a binary expression and the left operand is
+          `==' to the lhs then we have an OP= expression.  In this
+          case we must do some special processing.  */
+       if (TREE_CODE_CLASS (TREE_CODE (rhs)) == '2'
+           && lhs == TREE_OPERAND (rhs, 0))
+         {
+           if (TREE_CODE (lhs) == COMPONENT_REF)
+             {
+               tree field = TREE_OPERAND (lhs, 1);
+               if (! FIELD_STATIC (field))
+                 {
+                   /* Duplicate the object reference so we can get
+                      the field.  */
+                   emit_dup (TYPE_IS_WIDE (field) ? 2 : 1, 0, state);
+                   NOTE_POP (1);
+                 }
+               field_op (field, (FIELD_STATIC (field)
+                                 ? OPCODE_getstatic
+                                 : OPCODE_getfield),
+                         state);
+
+               NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (field)) ? 2 : 1);
+             }
+           else if (TREE_CODE (lhs) == VAR_DECL
+                    || TREE_CODE (lhs) == PARM_DECL)
+             {
+               if (FIELD_STATIC (lhs))
+                 {
+                   field_op (lhs, OPCODE_getstatic, state);
+                   NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (lhs)) ? 2 : 1);
+                 }
+               else
+                 emit_load (lhs, state);
+             }
+           else if (TREE_CODE (lhs) == ARRAY_REF)
+             {
+               /* Duplicate the array and index, which are on the
+                  stack, so that we can load the old value.  */
+               emit_dup (2, 0, state);
+               NOTE_POP (2);
+               jopcode = OPCODE_iaload + adjust_typed_op (TREE_TYPE (lhs), 7);
+               RESERVE (1);
+               OP1 (jopcode);
+               NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (lhs)) ? 2 : 1);
+             }
+           else
+             abort ();
+
+           /* This function correctly handles the case where the LHS
+              of a binary expression is NULL_TREE.  */
+           rhs = build (TREE_CODE (rhs), TREE_TYPE (rhs),
+                        NULL_TREE, TREE_OPERAND (rhs, 1));
+         }
+
        generate_bytecode_insns (rhs, STACK_TARGET, state);
        if (target != IGNORE_TARGET)
          emit_dup (TYPE_IS_WIDE (type) ? 2 : 1 , offset, state);
@@ -2054,12 +2137,12 @@ generate_bytecode_insns (exp, target, state)
       else if (TREE_CODE (exp) == ARRAY_REF)
        {
          jopcode = OPCODE_iastore + adjust_typed_op (TREE_TYPE (exp), 7);
-         RESERVE(1);
+         RESERVE (1);
          OP1 (jopcode);
          NOTE_POP (TYPE_IS_WIDE (TREE_TYPE (exp)) ? 4 : 3);
        }
       else
-       fatal ("internal error (bad lhs to MODIFY_EXPR)");
+       abort ();
       break;
     case PLUS_EXPR:
       jopcode = OPCODE_iadd;
@@ -2099,7 +2182,11 @@ generate_bytecode_insns (exp, target, state)
        }
       else
        {
-         generate_bytecode_insns (arg0, target, state);
+         /* ARG0 will be NULL_TREE if we're handling an `OP='
+            expression.  In this case the stack already holds the
+            LHS.  See the MODIFY_EXPR case.  */
+         if (arg0 != NULL_TREE)
+           generate_bytecode_insns (arg0, target, state);
          if (jopcode >= OPCODE_lshl && jopcode <= OPCODE_lushr)
            arg1 = convert (int_type_node, arg1);
          generate_bytecode_insns (arg1, target, state);
@@ -2207,78 +2294,6 @@ generate_bytecode_insns (exp, target, state)
       }
       break;
 
-    case CLEANUP_POINT_EXPR:
-      {
-       struct jcf_block *save_labeled_blocks = state->labeled_blocks;
-       int can_complete = CAN_COMPLETE_NORMALLY (TREE_OPERAND (exp, 0));
-       generate_bytecode_insns (TREE_OPERAND (exp, 0), IGNORE_TARGET, state);
-       if (target != IGNORE_TARGET)
-         abort ();
-       while (state->labeled_blocks != save_labeled_blocks)
-         {
-           struct jcf_block *finished_label = NULL;
-           tree return_link;
-           tree exception_type = build_pointer_type (throwable_type_node);
-           tree exception_decl = build_decl (VAR_DECL, NULL_TREE,
-                                             exception_type);
-           struct jcf_block *end_label = get_jcf_label_here (state);
-           struct jcf_block *label = state->labeled_blocks;
-           struct jcf_handler *handler;
-           tree cleanup = label->u.labeled_block;
-           state->labeled_blocks = label->next;
-           state->num_finalizers--;
-           if (can_complete)
-             {
-               finished_label = gen_jcf_label (state);
-               emit_jsr (label, state);
-               emit_goto (finished_label, state);
-               if (! CAN_COMPLETE_NORMALLY (cleanup))
-                 can_complete = 0;
-             }
-           handler = alloc_handler (label->v.start_label, end_label, state);
-           handler->type = NULL_TREE;
-           localvar_alloc (exception_decl, state);
-           NOTE_PUSH (1);
-            emit_store (exception_decl, state);
-           emit_jsr (label, state);
-           emit_load (exception_decl, state);
-           RESERVE (1);
-           OP1 (OPCODE_athrow);
-           NOTE_POP (1);
-
-           /* The finally block. */
-           return_link = build_decl (VAR_DECL, NULL_TREE,
-                                     return_address_type_node);
-           define_jcf_label (label, state);
-           NOTE_PUSH (1);
-           localvar_alloc (return_link, state);
-           emit_store (return_link, state);
-           generate_bytecode_insns (cleanup, IGNORE_TARGET, state);
-           maybe_wide (OPCODE_ret, DECL_LOCAL_INDEX (return_link), state);
-           localvar_free (return_link, state);
-           localvar_free (exception_decl, state);
-           if (finished_label != NULL)
-             define_jcf_label (finished_label, state);
-         }
-      }
-      break;
-
-    case WITH_CLEANUP_EXPR:
-      {
-       struct jcf_block *label;
-       generate_bytecode_insns (TREE_OPERAND (exp, 0), IGNORE_TARGET, state);
-       label = gen_jcf_label (state);
-       label->pc = PENDING_CLEANUP_PC;
-       label->next = state->labeled_blocks;
-       state->labeled_blocks = label;
-       state->num_finalizers++;
-       label->u.labeled_block = TREE_OPERAND (exp, 2);
-       label->v.start_label = get_jcf_label_here (state);
-       if (target != IGNORE_TARGET)
-         abort ();
-      }
-      break;
-
     case TRY_EXPR:
       {
        tree try_clause = TREE_OPERAND (exp, 0);
@@ -2290,13 +2305,16 @@ generate_bytecode_insns (exp, target, state)
          abort ();
        generate_bytecode_insns (try_clause, IGNORE_TARGET, state);
        end_label = get_jcf_label_here (state);
+       if (end_label == start_label)
+         break;
        if (CAN_COMPLETE_NORMALLY (try_clause))
          emit_goto (finished_label, state);
        while (clause != NULL_TREE)
          {
            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
@@ -2309,34 +2327,26 @@ generate_bytecode_insns (exp, target, state)
        define_jcf_label (finished_label, state);
       }
       break;
+
     case TRY_FINALLY_EXPR:
       {
-       struct jcf_block *finished_label, *finally_label, *start_label;
+       struct jcf_block *finished_label = NULL;
+       struct jcf_block *finally_label, *start_label, *end_label;
        struct jcf_handler *handler;
-       int worthwhile_finally = 1;
        tree try_block = TREE_OPERAND (exp, 0);
        tree finally = TREE_OPERAND (exp, 1);
-       tree return_link, exception_decl;
-
-       finally_label = start_label = NULL;
-       return_link = exception_decl = NULL_TREE;
-       finished_label = gen_jcf_label (state);
+       tree return_link = NULL_TREE, exception_decl = NULL_TREE;
 
-       /* If the finally clause happens to be empty, set a flag so we
-           remember to just skip it. */
-       if (BLOCK_EXPR_BODY (finally) == empty_stmt_node)
-         worthwhile_finally = 0;
+       tree exception_type;
 
-       if (worthwhile_finally)
+       finally_label = gen_jcf_label (state);
+       start_label = get_jcf_label_here (state);
+       /* 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))
          {
-           tree exception_type;
-           return_link = build_decl (VAR_DECL, NULL_TREE,
-                                     return_address_type_node);
-           exception_type = build_pointer_type (throwable_type_node);
-           exception_decl = build_decl (VAR_DECL, NULL_TREE, exception_type);
-
-           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;
@@ -2345,46 +2355,91 @@ generate_bytecode_insns (exp, target, state)
 
        generate_bytecode_insns (try_block, target, state);
 
-       if (worthwhile_finally)
+       if (CAN_COMPLETE_NORMALLY (finally))
          {
            if (state->labeled_blocks != finally_label)
              abort();
            state->labeled_blocks = finally_label->next;
-           emit_jsr (finally_label, state);
          }
+       end_label = get_jcf_label_here (state);
 
-       if (CAN_COMPLETE_NORMALLY (try_block)
-           && BLOCK_EXPR_BODY (try_block) != empty_stmt_node)
-         emit_goto (finished_label, state);
+       if (end_label == start_label)
+         {
+           state->num_finalizers--;
+           define_jcf_label (finally_label, state);
+           generate_bytecode_insns (finally, IGNORE_TARGET, state);
+           break;
+         }
 
-       /* Handle exceptions. */
+       if (CAN_COMPLETE_NORMALLY (finally))
+         {
+           return_link = build_decl (VAR_DECL, NULL_TREE,
+                                     return_address_type_node);
+           finished_label = gen_jcf_label (state);
+         }
 
-       if (!worthwhile_finally)
-         break;
+       if (CAN_COMPLETE_NORMALLY (try_block))
+         {
+           if (CAN_COMPLETE_NORMALLY (finally))
+             {
+               emit_jsr (finally_label, state);
+               emit_goto (finished_label, state);
+             }
+           else
+             emit_goto (finally_label, state);
+         }
 
-       localvar_alloc (return_link, state);
-       handler = alloc_handler (start_label, NULL_PTR, state);
-       handler->end_label = handler->handler_label;
+       /* Handle exceptions.  */
+
+       exception_type = build_pointer_type (throwable_type_node);
+       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);
-       localvar_free (exception_decl, state);
-
-       /* The finally block.  First save return PC into return_link. */
+       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.  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 (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:
@@ -2435,6 +2490,9 @@ generate_bytecode_insns (exp, target, state)
          }
       }
       break;
+    case JAVA_EXC_OBJ_EXPR:
+      NOTE_PUSH (1);  /* Pushed by exception system. */
+      break;
     case NEW_CLASS_EXPR:
       {
        tree class = TREE_TYPE (TREE_TYPE (exp));
@@ -2497,8 +2555,7 @@ generate_bytecode_insns (exp, target, state)
          }
        else if (f == soft_monitorenter_node
                 || f == soft_monitorexit_node
-                || f == throw_node[0]
-                || f == throw_node[1])
+                || f == throw_node)
          {
            if (f == soft_monitorenter_node)
              op = OPCODE_monitorenter;
@@ -2512,11 +2569,6 @@ generate_bytecode_insns (exp, target, state)
            NOTE_POP (1);
            break;
          }
-       else if (exp == soft_exceptioninfo_call_node)
-         {
-           NOTE_PUSH (1);  /* Pushed by exception system. */
-           break;
-         }
        for ( ;  x != NULL_TREE;  x = TREE_CHAIN (x))
          {
            generate_bytecode_insns (TREE_VALUE (x), STACK_TARGET, state);
@@ -2534,7 +2586,7 @@ generate_bytecode_insns (exp, target, state)
          NOTE_POP (1);  /* Pop implicit this. */
        if (TREE_CODE (f) == FUNCTION_DECL && DECL_CONTEXT (f) != NULL_TREE)
          {
-           tree saved_context = NULL_TREE;
+           tree context = DECL_CONTEXT (f);
            int index, interface = 0;
            RESERVE (5);
            if (METHOD_STATIC (f))
@@ -2542,23 +2594,30 @@ generate_bytecode_insns (exp, target, state)
            else if (DECL_CONSTRUCTOR_P (f) || CALL_USING_SUPER (exp)
                || METHOD_PRIVATE (f))
              OP1 (OPCODE_invokespecial);
-           else if (CLASS_INTERFACE (TYPE_NAME (DECL_CONTEXT (f))))
+           else
              {
-               OP1 (OPCODE_invokeinterface);
-               interface = 1;
+               if (CLASS_INTERFACE (TYPE_NAME (context)))
+                 {
+                   tree arg1 = TREE_VALUE (TREE_OPERAND (exp, 1));
+                   context = TREE_TYPE (TREE_TYPE (arg1));
+                   if (CLASS_INTERFACE (TYPE_NAME (context)))
+                     interface = 1;
+                 }
+               if (interface)
+                 OP1 (OPCODE_invokeinterface);
+               else
+                 OP1 (OPCODE_invokevirtual);
              }
-           else
-             OP1 (OPCODE_invokevirtual);
+           index = find_methodref_with_class_index (&state->cpool, f, context);
+           OP2 (index);
            if (interface)
              {
-               saved_context = DECL_CONTEXT (f);
-               DECL_CONTEXT (f) = 
-                 TREE_TYPE (TREE_TYPE (TREE_VALUE (TREE_OPERAND (exp, 1))));
+               if (nargs <= 0)
+                 abort ();
+
+               OP1 (nargs);
+               OP1 (0);
              }
-           index = find_methodref_index (&state->cpool, f);
-           if (interface)
-             DECL_CONTEXT (f) = saved_context;
-           OP2 (index);
            f = TREE_TYPE (TREE_TYPE (f));
            if (TREE_CODE (f) != VOID_TYPE)
              {
@@ -2568,11 +2627,6 @@ generate_bytecode_insns (exp, target, state)
                else
                  NOTE_PUSH (size);
              }
-           if (interface)
-             {
-               OP1 (nargs);
-               OP1 (0);
-             }
            break;
          }
       }
@@ -2676,7 +2730,7 @@ perform_relocations (state)
          /* new_ptr and old_ptr point into the old and new buffers,
             respectively.  (If no relocations cause the buffer to
             grow, the buffer will be the same buffer, and new_ptr==old_ptr.)
-            The bytes at higher adress have been copied and relocations
+            The bytes at higher address have been copied and relocations
             handled; those at lower addresses remain to process. */
 
          /* Lower old index of piece to be copied with no relocation.
@@ -2747,7 +2801,7 @@ perform_relocations (state)
            }
        }
       if (new_ptr != chunk->data)
-       fatal ("internal error - perform_relocations");
+       abort ();
     }
   state->code_length = pc;
 }
@@ -2869,8 +2923,11 @@ generate_classfile (clas, state)
       i = find_utf8_constant (&state->cpool, 
                              build_java_signature (TREE_TYPE (part)));
       PUT2(i);
-      have_value = DECL_INITIAL (part) != NULL_TREE && FIELD_STATIC (part)
-       && TREE_CODE (TREE_TYPE (part)) != POINTER_TYPE;
+      have_value = DECL_INITIAL (part) != NULL_TREE 
+       && FIELD_STATIC (part) && CONSTANT_VALUE_P (DECL_INITIAL (part))
+       && FIELD_FINAL (part)
+       && (JPRIMITIVE_TYPE_P (TREE_TYPE (part))
+           || TREE_TYPE (part) == string_ptr_type_node);
       if (have_value)
        attr_count++;
 
@@ -2882,6 +2939,8 @@ generate_classfile (clas, state)
        {
          tree init = DECL_INITIAL (part);
          static tree ConstantValue_node = NULL_TREE;
+         if (TREE_TYPE (part) != TREE_TYPE (init))
+           fatal_error ("field initializer type mismatch");
          ptr = append_chunk (NULL, 8, state);
          if (ConstantValue_node == NULL_TREE)
            ConstantValue_node = get_identifier ("ConstantValue");
@@ -2920,7 +2979,8 @@ generate_classfile (clas, state)
       i = (body != NULL_TREE) + (DECL_FUNCTION_THROWS (part) != NULL_TREE);
 
       /* Make room for the Synthetic attribute (of zero length.)  */
-      if (DECL_FINIT_P (part) 
+      if (DECL_FINIT_P (part)
+         || DECL_INSTINIT_P (part)
          || OUTER_FIELD_ACCESS_IDENTIFIER_P (DECL_NAME (part))
          || TYPE_DOT_CLASS (clas) == part)
        {
@@ -3083,18 +3143,26 @@ generate_classfile (clas, state)
     }
   ptr = append_chunk (NULL, 10, state);
 
-  i = ((INNER_CLASS_TYPE_P (clas) 
-       || DECL_INNER_CLASS_LIST (TYPE_NAME (clas))) ? 2 : 1);
+  i = 1;               /* Source file always exists as an attribute */
+  if (INNER_CLASS_TYPE_P (clas) || DECL_INNER_CLASS_LIST (TYPE_NAME (clas)))
+    i++;
+  if (clas == object_type_node)
+    i++;
   PUT2 (i);                    /* attributes_count */
 
   /* generate the SourceFile attribute. */
   if (SourceFile_node == NULL_TREE) 
-    SourceFile_node = get_identifier ("SourceFile");
+    {
+      SourceFile_node = get_identifier ("SourceFile");
+      ggc_add_tree_root (&SourceFile_node, 1);
+    }
+
   i = find_utf8_constant (&state->cpool, SourceFile_node);
   PUT2 (i);  /* attribute_name_index */
   PUT4 (2);
   i = find_utf8_constant (&state->cpool, get_identifier (source_file));
   PUT2 (i);
+  append_gcj_attribute (state, clas);
   append_innerclasses_attribute (state, clas);
 
   /* New finally generate the contents of the constant pool chunk. */
@@ -3115,7 +3183,10 @@ append_synthetic_attribute (state)
   int i;
 
   if (Synthetic_node == NULL_TREE)
-    Synthetic_node = get_identifier ("Synthetic");
+    {
+      Synthetic_node = get_identifier ("Synthetic");
+      ggc_add_tree_root (&Synthetic_node, 1);
+    }
   i = find_utf8_constant (&state->cpool, Synthetic_node);
   PUT2 (i);            /* Attribute string index */
   PUT4 (0);            /* Attribute length */
@@ -3124,6 +3195,24 @@ append_synthetic_attribute (state)
 }
 
 static void
+append_gcj_attribute (state, class)
+     struct jcf_partial *state;
+     tree class;
+{
+  unsigned char *ptr;
+  int i;
+
+  if (class != object_type_node)
+    return;
+
+  ptr = append_chunk (NULL, 6, state); /* 2+4 */
+  i = find_utf8_constant (&state->cpool, 
+                         get_identifier ("gnu.gcj.gcj-compiled"));
+  PUT2 (i);                    /* Attribute string index */
+  PUT4 (0);                    /* Attribute length */
+}
+
+static void
 append_innerclasses_attribute (state, class)
      struct jcf_partial *state;
      tree class;
@@ -3139,8 +3228,11 @@ append_innerclasses_attribute (state, class)
 
   ptr = append_chunk (NULL, 8, state); /* 2+4+2 */
   
-  if (InnerClasses_node == NULL_TREE)
-    InnerClasses_node = get_identifier ("InnerClasses");
+  if (InnerClasses_node == NULL_TREE) 
+    {
+      InnerClasses_node = get_identifier ("InnerClasses");
+      ggc_add_tree_root (&InnerClasses_node, 1);
+    }
   i = find_utf8_constant (&state->cpool, InnerClasses_node);
   PUT2 (i);
   length_marker = ptr; PUT4 (0); /* length, to be later patched */
@@ -3150,7 +3242,7 @@ append_innerclasses_attribute (state, class)
      process: itself, up and down. */
   while (class && INNER_CLASS_TYPE_P (class))
     {
-      char *n;
+      const char *n;
 
       decl = TYPE_NAME (class);
       n = IDENTIFIER_POINTER (DECL_NAME (decl)) + 
@@ -3182,24 +3274,24 @@ append_innerclasses_attribute_entry (state, decl, name)
      struct jcf_partial *state;
      tree decl, name;
 {
-  static tree anonymous_name = NULL_TREE;
-  int icii, ocii, ini, icaf;
+  int icii, icaf;
+  int ocii = 0, ini = 0;
   unsigned char *ptr = append_chunk (NULL, 8, state);
 
-  if (!anonymous_name)
-    anonymous_name = get_identifier ("");
-  
   icii = find_class_constant (&state->cpool, TREE_TYPE (decl));
-  ocii = find_class_constant (&state->cpool, TREE_TYPE (DECL_CONTEXT (decl))); 
-
-  /* The specs are saying that if the class is anonymous,
-     inner_name_index must be zero. But the implementation makes it
-     point to an empty string. */
-  ini = find_utf8_constant (&state->cpool,
-                           (ANONYMOUS_CLASS_P (TREE_TYPE (decl)) ? 
-                            anonymous_name : name));
+
+  /* Sun's implementation seems to generate ocii to 0 for inner
+     classes (which aren't considered members of the class they're
+     in.) The specs are saying that if the class is anonymous,
+     inner_name_index must be zero. */
+  if (!ANONYMOUS_CLASS_P (TREE_TYPE (decl)))
+    {
+      ocii = find_class_constant (&state->cpool, 
+                                 TREE_TYPE (DECL_CONTEXT (decl)));
+      ini = find_utf8_constant (&state->cpool, name);
+    }
   icaf = get_access_flags (decl);
-  
+
   PUT2 (icii); PUT2 (ocii); PUT2 (ini);  PUT2 (icaf);
 }
 
@@ -3207,8 +3299,8 @@ static char *
 make_class_file_name (clas)
      tree clas;
 {
-  const char *dname, *slash;
-  char *cname, *r;
+  const char *dname, *cname, *slash;
+  char *r;
   struct stat sb;
 
   cname = IDENTIFIER_POINTER (identifier_subst (DECL_NAME (TYPE_NAME (clas)),
@@ -3250,23 +3342,18 @@ make_class_file_name (clas)
   dname = r + (slash - dname) + 1;
   while (1)
     {
-      cname = strchr (dname, DIR_SEPARATOR);
-      if (cname == NULL)
+      char *s = strchr (dname, DIR_SEPARATOR);
+      if (s == NULL)
        break;
-      *cname = '\0';
-      if (stat (r, &sb) == -1)
-       {
+      *s = '\0';
+      if (stat (r, &sb) == -1
          /* Try to make it.  */
-         if (mkdir (r, 0755) == -1)
-           {
-             fatal ("failed to create directory `%s'", r);
-             free (r);
-             return NULL;
-           }
-       }
-      *cname = DIR_SEPARATOR;
+         && mkdir (r, 0755) == -1)
+       fatal_io_error ("can't create directory %s", r);
+
+      *s = DIR_SEPARATOR;
       /* Skip consecutive separators.  */
-      for (dname = cname + 1; *dname && *dname == DIR_SEPARATOR; ++dname)
+      for (dname = s + 1; *dname && *dname == DIR_SEPARATOR; ++dname)
        ;
     }
 
@@ -3287,15 +3374,16 @@ write_classfile (clas)
 
   if (class_file_name != NULL)
     {
-      FILEstream = fopen (class_file_name, "wb");
+      FILE *stream = fopen (class_file_name, "wb");
       if (stream == NULL)
-       fatal ("failed to open `%s' for writing", class_file_name);
+       fatal_io_error ("can't open %s for writing", class_file_name);
+
       jcf_dependency_add_target (class_file_name);
       init_jcf_state (state, work);
       chunks = generate_classfile (clas, state);
       write_chunks (stream, chunks);
       if (fclose (stream))
-       fatal ("failed to close after writing `%s'", class_file_name);
+       fatal_io_error ("error closing %s", class_file_name);
       free (class_file_name);
     }
   release_jcf_state (state);