Various patches to emit better messages on verification errors.
authorbothner <bothner@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 14 Jan 2001 21:48:10 +0000 (21:48 +0000)
committerbothner <bothner@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 14 Jan 2001 21:48:10 +0000 (21:48 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@39019 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/java/ChangeLog
gcc/java/expr.c
gcc/java/java-tree.h
gcc/java/verify.c

index 1a00840..538d4b1 100644 (file)
@@ -1,5 +1,20 @@
 2001-01-14  Per Bothner  <per@bothner.com>
 
+       Various patches to emit better messages on verification errors.
+       * expr.c (push_type_0):  Return error indication on stack overflow,
+       instead of callinfg fatal.
+       (push_type):  Now just call push_type_0 (nd fatal on overflow).
+       (pop_type_0):  Return detailed error message (in a char** argument).
+       (pop_type):  If pop_type_0 fails, print error message.
+       (pop_argument_types):  Moved to verify.c.
+       * verify.c (pop_argument_types):  Moved from expr.c.
+       Return a (possible) error message, rather than void.
+       (POP_TYPE, POP_TYPE_CONV, PUSH_TYPE, PUSH_PENDING):  New macros.
+       (verify_jvm_instruction):  Use new macros, improving error messages.
+       For case OPCODE_astore use object_ptr_type_node.
+       * java-tree.h (TYPE_UNDERFLOW, TYPE_UNEXPECTED):  New macros.
+       (pop_type_0, push_type_0, pop_argument_types):  Update accordingly.
+
        * parse.y (java_complete_lhs case EXPR_WITH_FILE_LOCATION): If body is
        constant, return body without wrapper.  (Improves constant folding.)
        * lex.c (build_wfl_node):  Clear TREE_TYPE from returned node.
index ddfc473..abc5f69 100644 (file)
@@ -256,19 +256,31 @@ flush_quick_stack ()
     }
 }
 
-void
-push_type (type)
+/* Push TYPE on the type stack.
+   Return true on success, 0 on overflow. */
+
+int
+push_type_0 (type)
      tree type;
 {
   int n_words;
   type = promote_type (type);
   n_words = 1 + TYPE_IS_WIDE (type);
   if (stack_pointer + n_words > DECL_MAX_STACK (current_function_decl))
-    fatal ("stack overflow");
+    return 0;
   stack_type_map[stack_pointer++] = type;
   n_words--;
   while (--n_words >= 0)
     stack_type_map[stack_pointer++] = TYPE_SECOND;
+  return 1;
+}
+
+void
+push_type (type)
+     tree type;
+{
+  if (! push_type_0 (type))
+    fatal ("stack overflow");
 }
 
 static void
@@ -296,23 +308,32 @@ push_value (value)
 
 /* Pop a type from the type stack.
    TYPE is the expected type.   Return the actual type, which must be
-   convertible to TYPE, otherwise NULL_TREE is returned. */
+   convertible to TYPE.
+   On an error, *MESSAGEP is set to a freshly malloc'd error message. */
 
 tree
-pop_type_0 (type)
+pop_type_0 (type, messagep)
      tree type;
+     char **messagep;
 {
   int n_words;
   tree t;
+  *messagep = NULL;
   if (TREE_CODE (type) == RECORD_TYPE)
     type = promote_type (type);
   n_words = 1 + TYPE_IS_WIDE (type);
   if (stack_pointer < n_words)
-    fatal ("stack underflow");
+    {
+      *messagep = xstrdup ("stack underflow");
+      return type;
+    }
   while (--n_words > 0)
     {
       if (stack_type_map[--stack_pointer] != void_type_node)
-       fatal ("Invalid multi-word value on type stack");
+       {
+         *messagep = xstrdup ("Invalid multi-word value on type stack");
+         return type;
+       }
     }
   t = stack_type_map[--stack_pointer];
   if (type == NULL_TREE || t == type)
@@ -334,7 +355,24 @@ pop_type_0 (type)
       /* FIXME: this is worse than a kludge, probably.  */
       return object_ptr_type_node;
     }
-  return NULL_TREE;
+  {
+    const char *str1 = "expected type '";
+    const char *str3 = "' but stack contains '";
+    const char *str5 = "'";
+    int len1 = strlen (str1);
+    int len2 = strlen (lang_printable_name (type, 0));
+    int len3 = strlen (str3);
+    int len4 = strlen (lang_printable_name (t, 0));
+    int len5 = strlen (str5);
+    char *msg = xmalloc (len1 + len2 + len3 + len4 + len5 + 1);
+    *messagep = msg;
+    strcpy (msg, str1);  msg += len1;
+    strcpy (msg, lang_printable_name (type, 0));  msg += len2;
+    strcpy (msg, str3);  msg += len3;
+    strcpy (msg, lang_printable_name (t, 0));  msg += len4;
+    strcpy (msg, str5);
+    return type;
+  }
 }
 
 /* Pop a type from the type stack.
@@ -345,10 +383,13 @@ tree
 pop_type (type)
      tree type;
 {
-  tree t = pop_type_0 (type);
-  if (t != NULL_TREE)
-    return t;
-  error ("unexpected type on stack");
+  char *message = NULL;
+  type = pop_type_0 (type, &message);
+  if (message != NULL)
+    {
+      error (message);
+      free (message);
+    }
   return type;
 }
 
@@ -1576,23 +1617,6 @@ expand_java_ret (return_address)
 }
 #endif
 
-/* Recursive helper function to pop argument types during verifiation. */
-
-void
-pop_argument_types (arg_types)
-     tree arg_types;
-{
-  if (arg_types == end_params_node)
-    return;
-  if (TREE_CODE (arg_types) == TREE_LIST)
-    {
-      pop_argument_types (TREE_CHAIN (arg_types));
-      pop_type (TREE_VALUE (arg_types));
-      return;
-    }
-  abort ();
-}
-
 static tree
 pop_arguments (arg_types)
      tree arg_types;
index 510511e..3d0793d 100644 (file)
@@ -978,9 +978,8 @@ extern tree build_java_array_type PARAMS ((tree, HOST_WIDE_INT));
 extern int is_compiled_class PARAMS ((tree));
 extern tree mangled_classname PARAMS ((const char*, tree));
 extern tree lookup_label PARAMS ((int));
-extern tree pop_type_0 PARAMS ((tree));
+extern tree pop_type_0 PARAMS ((tree, char**));
 extern tree pop_type PARAMS ((tree));
-extern void pop_argument_types PARAMS ((tree));
 extern tree decode_newarray_type PARAMS ((int));
 extern tree lookup_field PARAMS ((tree*, tree));
 extern int is_array_type_p PARAMS ((tree));
@@ -1057,6 +1056,7 @@ extern int process_jvm_instruction PARAMS ((int, const unsigned char *, long));
 extern int maybe_adjust_start_pc PARAMS ((struct JCF *, int, int, int));
 extern void set_local_type PARAMS ((int, tree));
 extern int merge_type_state PARAMS ((tree));
+extern int push_type_0 PARAMS ((tree));
 extern void push_type PARAMS ((tree));
 extern void load_type_state PARAMS ((tree));
 extern void add_interface PARAMS ((tree, tree));
@@ -1244,6 +1244,12 @@ extern int linenumber_count;
    used nor set in the subroutine. */
 #define TYPE_UNUSED error_mark_node
 
+/* When returned from pop_type_0, indicates stack underflow. */
+#define TYPE_UNDERFLOW integer_zero_node
+
+/* When returned from pop_type_0, indicates a type mismatch. */
+#define TYPE_UNEXPECTED NULL_TREE
+
 /* A array mapping variable/stack slot index to the type current
    in that variable/stack slot.
    TYPE_UNKNOWN, TYPE_SECOND, and TYPE_NULL are special cases. */
index 463509a..23b3088 100644 (file)
@@ -348,9 +348,43 @@ start_pc_cmp (xp, yp)
 #define VERIFICATION_ERROR(MESSAGE) \
   do { message = MESSAGE;  goto verify_error; } while (0)
 
+/* Recursive helper function to pop argument types during verifiation.
+   ARG_TYPES is the list of formal parameter types.
+   Return NULL on success and a freshly malloc'd error message on failure. */
+
+static char *
+pop_argument_types (arg_types)
+     tree arg_types;
+{
+  if (arg_types == end_params_node)
+    return NULL;
+  if (TREE_CODE (arg_types) == TREE_LIST)
+    {
+      char *message = pop_argument_types (TREE_CHAIN (arg_types));
+      if (message == NULL)
+       pop_type_0 (TREE_VALUE (arg_types), &message);
+      return message;
+    }
+  abort ();
+}
+
+#define POP_TYPE(TYPE, MESSAGE) \
+  do { pmessage = NULL;  pop_type_0 (TYPE, &pmessage); \
+       if (pmessage != NULL) goto pop_type_error; \
+  } while (0)
+
+#define POP_TYPE_CONV(TYPE, POPPED_TYPE, MESSAGE) \
+  do { pmessage = NULL;  POPPED_TYPE = pop_type_0 (TYPE, &pmessage); \
+       if (pmessage != NULL) goto pop_type_error; \
+  } while (0)
+
+#define PUSH_TYPE(TYPE) \
+  do { if (! push_type_0 (TYPE)) { goto stack_overflow; }} while (0)
+
 #define PUSH_PENDING(LABEL) \
-     do { if ((message = check_pending_block (LABEL)) != NULL) \
-             goto verify_error; } while (0)
+     do { tree tmplab = LABEL; \
+          if ((message = check_pending_block (tmplab)) != NULL) \
+            { oldpc = LABEL_PC (tmplab); goto verify_error; }} while (0)
 
 #ifdef __GNUC__
 #define CHECK_PC_IN_RANGE(PC) ({if (PC < 0 || PC > length) goto bad_pc; (void)1;})
@@ -376,6 +410,7 @@ verify_jvm_instructions (jcf, byte_ops, length)
   int oldpc = 0; /* PC of start of instruction. */
   int prevpc = 0;  /* If >= 0, PC of previous instruction. */
   const char *message;
+  char *pmessage;
   int i;
   register unsigned char *p;
   struct eh_range *prev_eh_ranges = NULL_EH_RANGE;
@@ -559,13 +594,13 @@ verify_jvm_instructions (jcf, byte_ops, length)
          if (byte_ops[PC] == OPCODE_newarray
              || byte_ops[PC] == OPCODE_newarray)
            int_value = i;
-         push_type (int_type_node);  break;
+         PUSH_TYPE (int_type_node);  break;
        case OPCODE_lconst_0:   case OPCODE_lconst_1:
-         push_type (long_type_node);  break;
+         PUSH_TYPE (long_type_node);  break;
        case OPCODE_fconst_0:   case OPCODE_fconst_1:   case OPCODE_fconst_2:
-         push_type (float_type_node);  break;
+         PUSH_TYPE (float_type_node);  break;
        case OPCODE_dconst_0:   case OPCODE_dconst_1:
-         push_type (double_type_node);  break;
+         PUSH_TYPE (double_type_node);  break;
        case OPCODE_bipush:
          i = IMMEDIATE_s1;
          goto push_int;
@@ -616,13 +651,13 @@ verify_jvm_instructions (jcf, byte_ops, length)
                ? (! INTEGRAL_TYPE_P (tmp) || TYPE_PRECISION (tmp) > 32)
                : type != tmp))
          VERIFICATION_ERROR("invalid local variable type in load");
-       push_type (tmp);
+       PUSH_TYPE (tmp);
        goto note_used;
        case OPCODE_istore:  type = int_type_node;  goto general_store;
        case OPCODE_lstore:  type = long_type_node;  goto general_store;
        case OPCODE_fstore:  type = float_type_node;  goto general_store;
        case OPCODE_dstore:  type = double_type_node;  goto general_store;
-       case OPCODE_astore:  type = ptr_type_node;  goto general_store;
+       case OPCODE_astore:  type = object_ptr_type_node;  goto general_store;
        general_store:
        index = wide ? IMMEDIATE_u2 : IMMEDIATE_u1;
        wide = 0;
@@ -655,7 +690,7 @@ verify_jvm_instructions (jcf, byte_ops, length)
            VERIFICATION_ERROR ("invalid local variable index in store");
            return 0;
          }
-       type = pop_type (type);
+       POP_TYPE_CONV (type, type, NULL);
        type_map[index] = type;
 
        /* If local variable changed, we need to reconsider eh handlers. */
@@ -723,19 +758,19 @@ verify_jvm_instructions (jcf, byte_ops, length)
          type = double_type_node;  goto unop;
        unop:
          pop_type (type);
-         push_type (type);
+         PUSH_TYPE (type);
          break;
        binop:
          pop_type (type);
          pop_type (type);
-         push_type (type);
+         PUSH_TYPE (type);
          break;
        case OPCODE_lshl:
        case OPCODE_lshr:
        case OPCODE_lushr:
          pop_type (int_type_node);
          pop_type (long_type_node);
-         push_type (long_type_node);
+         PUSH_TYPE (long_type_node);
          break;
        case OPCODE_iinc:
          index = wide ? IMMEDIATE_u2 : IMMEDIATE_u1;
@@ -744,33 +779,34 @@ verify_jvm_instructions (jcf, byte_ops, length)
          if (index < 0 || index >= DECL_MAX_LOCALS (current_function_decl))
            VERIFICATION_ERROR ("invalid local variable index in iinc");
          tmp = type_map[index];
-         if (! INTEGRAL_TYPE_P (tmp) || TYPE_PRECISION (tmp) > 32)
+         if (tmp == NULL_TREE
+             || ! INTEGRAL_TYPE_P (tmp) || TYPE_PRECISION (tmp) > 32)
            VERIFICATION_ERROR ("invalid local variable type in iinc");
          break;
        case OPCODE_i2l:
-         pop_type (int_type_node);    push_type (long_type_node);   break;
+         pop_type (int_type_node);    PUSH_TYPE (long_type_node);   break;
        case OPCODE_i2f:
-         pop_type (int_type_node);    push_type (float_type_node);  break;
+         pop_type (int_type_node);    PUSH_TYPE (float_type_node);  break;
        case OPCODE_i2d:
-         pop_type (int_type_node);    push_type (double_type_node); break;
+         pop_type (int_type_node);    PUSH_TYPE (double_type_node); break;
        case OPCODE_l2i:
-         pop_type (long_type_node);   push_type (int_type_node);    break;
+         pop_type (long_type_node);   PUSH_TYPE (int_type_node);    break;
        case OPCODE_l2f:
-         pop_type (long_type_node);   push_type (float_type_node);  break;
+         pop_type (long_type_node);   PUSH_TYPE (float_type_node);  break;
        case OPCODE_l2d:
-         pop_type (long_type_node);   push_type (double_type_node); break;
+         pop_type (long_type_node);   PUSH_TYPE (double_type_node); break;
        case OPCODE_f2i:
-         pop_type (float_type_node);  push_type (int_type_node);    break;
+         pop_type (float_type_node);  PUSH_TYPE (int_type_node);    break;
        case OPCODE_f2l:
-         pop_type (float_type_node);  push_type (long_type_node);   break;
+         pop_type (float_type_node);  PUSH_TYPE (long_type_node);   break;
        case OPCODE_f2d:
-         pop_type (float_type_node);  push_type (double_type_node); break;
+         pop_type (float_type_node);  PUSH_TYPE (double_type_node); break;
        case OPCODE_d2i:
-         pop_type (double_type_node); push_type (int_type_node);    break;
+         pop_type (double_type_node); PUSH_TYPE (int_type_node);    break;
        case OPCODE_d2l:
-         pop_type (double_type_node); push_type (long_type_node);   break;
+         pop_type (double_type_node); PUSH_TYPE (long_type_node);   break;
        case OPCODE_d2f:
-         pop_type (double_type_node); push_type (float_type_node);  break;
+         pop_type (double_type_node); PUSH_TYPE (float_type_node);  break;
        case OPCODE_lcmp:
          type = long_type_node;  goto compare;
        case OPCODE_fcmpl:
@@ -781,7 +817,7 @@ verify_jvm_instructions (jcf, byte_ops, length)
          type = double_type_node;  goto compare;
        compare:
          pop_type (type);  pop_type (type);
-         push_type (int_type_node);  break;
+         PUSH_TYPE (int_type_node);  break;
        case OPCODE_ifeq:
        case OPCODE_ifne:
        case OPCODE_iflt:
@@ -848,10 +884,7 @@ verify_jvm_instructions (jcf, byte_ops, length)
          if (type != return_type)
            VERIFICATION_ERROR ("incorrect ?return opcode");
          if (type != void_type_node)
-           {
-             if (pop_type_0 (type) == NULL_TREE)
-               VERIFICATION_ERROR ("return value has wrong type");
-           }
+           POP_TYPE(type, "return value has wrong type");
          INVALIDATE_PC;
          break;
        case OPCODE_getstatic: is_putting = 0;  is_static = 1;  goto field;
@@ -864,22 +897,21 @@ verify_jvm_instructions (jcf, byte_ops, length)
            tree field_signature = COMPONENT_REF_SIGNATURE (&current_jcf->cpool, index);
            tree field_type = get_type_from_signature (field_signature);
            if (is_putting)
-             pop_type (field_type);
+             POP_TYPE (field_type, "incorrect type for field");
            if (! is_static)
              {
                int clindex = COMPONENT_REF_CLASS_INDEX (&current_jcf->cpool,
                                                        index);
                tree self_type = get_class_constant (current_jcf, clindex);
                /* Defer actual checking until next pass. */
-               if (pop_type_0 (self_type) == NULL_TREE)
-                 VERIFICATION_ERROR ("incorrect type for field reference");
+               POP_TYPE(self_type, "incorrect type for field reference");
              }
            if (! is_putting)
-             push_type (field_type);
+             PUSH_TYPE (field_type);
            break;
          }
        case OPCODE_new:
-         push_type (get_class_constant (jcf, IMMEDIATE_u2));
+         PUSH_TYPE (get_class_constant (jcf, IMMEDIATE_u2));
          break;
        case OPCODE_dup:     type_stack_dup (1, 0);  break;
        case OPCODE_dup_x1:  type_stack_dup (1, 1);  break;
@@ -934,7 +966,7 @@ verify_jvm_instructions (jcf, byte_ops, length)
              i = TREE_INT_CST_LOW (get_constant (current_jcf, index));
              goto push_int;
            }
-         push_type (type);
+         PUSH_TYPE (type);
          break;
 
        case OPCODE_invokevirtual:
@@ -953,7 +985,12 @@ verify_jvm_instructions (jcf, byte_ops, length)
                                                  IDENTIFIER_LENGTH (sig));
            if (TREE_CODE (method_type) != FUNCTION_TYPE)
              VERIFICATION_ERROR ("bad method signature");
-           pop_argument_types (TYPE_ARG_TYPES (method_type));
+           pmessage = pop_argument_types (TYPE_ARG_TYPES (method_type));
+           if (pmessage != NULL)
+             {
+               message = "invalid argument type";
+               goto pop_type_error;
+             }
 
            /* Can't invoke <clinit> */
            if (ID_CLINIT_P (method_name))
@@ -963,7 +1000,8 @@ verify_jvm_instructions (jcf, byte_ops, length)
              VERIFICATION_ERROR ("invoke opcode can't invoke <init>");
 
            if (op_code != OPCODE_invokestatic)
-             pop_type (self_type);
+             POP_TYPE (self_type,
+                       "stack type not subclass of invoked method's class");
 
            switch (op_code)
              {
@@ -980,14 +1018,14 @@ verify_jvm_instructions (jcf, byte_ops, length)
              }
 
            if (TREE_TYPE (method_type) != void_type_node)
-             push_type (TREE_TYPE (method_type));
+             PUSH_TYPE (TREE_TYPE (method_type));
            break;
          }
 
        case OPCODE_arraylength:
            /* Type checking actually made during code generation */
            pop_type( ptr_type_node );
-           push_type( int_type_node );
+           PUSH_TYPE( int_type_node );
            break;
            
         /* Q&D verification *or* more checking done during code generation
@@ -1025,7 +1063,7 @@ verify_jvm_instructions (jcf, byte_ops, length)
            type = TYPE_ARRAY_ELEMENT (TREE_TYPE (tmp));
          else if (tmp != TYPE_NULL)
            VERIFICATION_ERROR ("array load from non-array type");
-         push_type (type);
+         PUSH_TYPE (type);
          break;
 
        case OPCODE_anewarray:
@@ -1061,7 +1099,7 @@ verify_jvm_instructions (jcf, byte_ops, length)
            int_value = -1;
          type = build_java_array_type (type, int_value);
          pop_type (int_type_node);
-         push_type (type);
+         PUSH_TYPE (type);
          break;
 
        case OPCODE_multianewarray:
@@ -1075,12 +1113,12 @@ verify_jvm_instructions (jcf, byte_ops, length)
 
            for( i = 0; i < ndim; i++ )
              pop_type (int_type_node);
-           push_type (get_class_constant (current_jcf, index));
+           PUSH_TYPE (get_class_constant (current_jcf, index));
            break;
          }
 
        case OPCODE_aconst_null:
-         push_type (ptr_type_node);
+         PUSH_TYPE (ptr_type_node);
          break;
 
        case OPCODE_athrow:
@@ -1092,12 +1130,12 @@ verify_jvm_instructions (jcf, byte_ops, length)
        case OPCODE_checkcast:
          pop_type (ptr_type_node);
          type = get_class_constant (current_jcf, IMMEDIATE_u2);
-         push_type (type);
+         PUSH_TYPE (type);
          break;
        case OPCODE_instanceof:
          pop_type (ptr_type_node);
          get_class_constant (current_jcf, IMMEDIATE_u2);
-         push_type (int_type_node);
+         PUSH_TYPE (int_type_node);
          break;
 
        case OPCODE_tableswitch:
@@ -1170,7 +1208,7 @@ verify_jvm_instructions (jcf, byte_ops, length)
          {
            tree target = lookup_label (oldpc + IMMEDIATE_s2);
            tree return_label = lookup_label (PC);
-           push_type (return_address_type_node);
+           PUSH_TYPE (return_address_type_node);
            /* The return label chain will be null if this is the first
               time we've seen this jsr target.  */
             if (LABEL_RETURN_LABEL (target) == NULL_TREE)
@@ -1358,6 +1396,16 @@ verify_jvm_instructions (jcf, byte_ops, length)
        }
     }
   return 1;
+ pop_type_error:
+  error ("verification error at PC=%d", oldpc);
+  if (message != NULL)
+    error ("%s", message);
+  error ("%s", pmessage);
+  free (pmessage);
+  return 0;
+ stack_overflow:
+  message = "stack overflow";
+  goto verify_error;
  bad_pc:
   message = "program counter out of range";
   goto verify_error;