OSDN Git Service

2005-05-18 Thomas Koenig <Thomas.Koenig@online.de>
[pf3gnuchains/gcc-fork.git] / libjava / verify.cc
index 8c037ed..a47571b 100644 (file)
@@ -1,6 +1,6 @@
 // verify.cc - verify bytecode
 
-/* Copyright (C) 2001, 2002, 2003, 2004  Free Software Foundation
+/* Copyright (C) 2001, 2002, 2003, 2004, 2005  Free Software Foundation
 
    This file is part of libgcj.
 
@@ -19,6 +19,11 @@ details.  */
 #include <java-insns.h>
 #include <java-interp.h>
 
+// On Solaris 10/x86, <signal.h> indirectly includes <ia32/sys/reg.h>, which 
+// defines PC since g++ predefines __EXTENSIONS__.  Undef here to avoid clash
+// with PC member of class _Jv_BytecodeVerifier below.
+#undef PC
+
 #ifdef INTERPRETER
 
 #include <java/lang/Class.h>
@@ -26,6 +31,7 @@ details.  */
 #include <java/lang/Throwable.h>
 #include <java/lang/reflect/Modifier.h>
 #include <java/lang/StringBuffer.h>
+#include <java/lang/NoClassDefFoundError.h>
 
 #ifdef VERIFY_DEBUG
 #include <stdio.h>
@@ -40,7 +46,7 @@ static void debug_print (const char *fmt, ...)
   __attribute__ ((format (printf, 1, 2)));
 
 static inline void
-debug_print (const char *fmt, ...)
+debug_print (MAYBE_UNUSED const char *fmt, ...)
 {
 #ifdef VERIFY_DEBUG
   va_list ap;
@@ -95,13 +101,15 @@ debug_print (const char *fmt, ...)
 // subroutine is exited via `goto' or `athrow' and not `ret'.
 //
 // In some other areas the JVM specification is (mildly) incorrect,
-// but we still implement what is specified.  For instance, you cannot
+// so we diverge.  For instance, you cannot
 // violate type safety by allocating an object with `new' and then
 // failing to initialize it, no matter how one branches or where one
 // stores the uninitialized reference.  See "Improving the official
 // specification of Java bytecode verification" by Alessandro Coglio.
-// Similarly, there's no real point in enforcing that padding bytes or
-// the mystery byte of invokeinterface must be 0, but we do that too.
+//
+// Note that there's no real point in enforcing that padding bytes or
+// the mystery byte of invokeinterface must be 0, but we do that
+// regardless.
 //
 // The verifier is currently neither completely lazy nor eager when it
 // comes to loading classes.  It tries to represent types by name when
@@ -174,28 +182,21 @@ private:
   // This method.
   _Jv_InterpMethod *current_method;
 
-  // A linked list of utf8 objects we allocate.  This is really ugly,
-  // but without this our utf8 objects would be collected.
+  // A linked list of utf8 objects we allocate.
   linked<_Jv_Utf8Const> *utf8_list;
 
   // A linked list of all ref_intersection objects we allocate.
   ref_intersection *isect_list;
 
   // Create a new Utf-8 constant and return it.  We do this to avoid
-  // having our Utf-8 constants prematurely collected.  FIXME this is
-  // ugly.
+  // having our Utf-8 constants prematurely collected.
   _Jv_Utf8Const *make_utf8_const (char *s, int len)
   {
-    _Jv_Utf8Const *val = _Jv_makeUtf8Const (s, len);
-    _Jv_Utf8Const *r = (_Jv_Utf8Const *) _Jv_Malloc (sizeof (_Jv_Utf8Const)
-                                                    + val->length
-                                                    + 1);
-    r->length = val->length;
-    r->hash = val->hash;
-    memcpy (r->data, val->data, val->length + 1);
-
-    linked<_Jv_Utf8Const> *lu
-      = (linked<_Jv_Utf8Const> *) _Jv_Malloc (sizeof (linked<_Jv_Utf8Const>));
+    linked<_Jv_Utf8Const> *lu = (linked<_Jv_Utf8Const> *)
+      _Jv_Malloc (sizeof (linked<_Jv_Utf8Const>)
+                 + _Jv_Utf8Const::space_needed(s, len));
+    _Jv_Utf8Const *r = (_Jv_Utf8Const *) (lu + 1);
+    r->init(s, len);
     lu->val = r;
     lu->next = utf8_list;
     utf8_list = lu;
@@ -221,9 +222,9 @@ private:
     buf->append (JvNewStringLatin1 (" in "));
     buf->append (current_class->getName());
     buf->append ((jchar) ':');
-    buf->append (JvNewStringUTF (method->get_method()->name->data));
+    buf->append (method->get_method()->name->toString());
     buf->append ((jchar) '(');
-    buf->append (JvNewStringUTF (method->get_method()->signature->data));
+    buf->append (method->get_method()->signature->toString());
     buf->append ((jchar) ')');
 
     buf->append (JvNewStringLatin1 (": "));
@@ -367,9 +368,12 @@ private:
       java::lang::ClassLoader *loader
        = verifier->current_class->getClassLoaderInternal();
       // We might see either kind of name.  Sigh.
-      if (data.name->data[0] == 'L'
-         && data.name->data[data.name->length - 1] == ';')
-       data.klass = _Jv_FindClassFromSignature (data.name->data, loader);
+      if (data.name->first() == 'L' && data.name->limit()[-1] == ';')
+       {
+         data.klass = _Jv_FindClassFromSignature (data.name->chars(), loader);
+         if (data.klass == NULL)
+           throw new java::lang::NoClassDefFoundError(data.name->toString());
+       }
       else
        data.klass = Class::forName (_Jv_NewStringUtf8Const (data.name),
                                     false, loader);
@@ -417,7 +421,7 @@ private:
       if (is_resolved)
        return data.klass->isArray ();
       else
-       return data.name->data[0] == '[';
+       return data.name->first() == '[';
     }
 
     bool isinterface (_Jv_BytecodeVerifier *verifier)
@@ -458,7 +462,7 @@ private:
        }
       else
        {
-         char *p = data.name->data;
+         char *p = data.name->chars();
          while (*p++ == '[')
            ++ndims;
        }
@@ -554,7 +558,7 @@ private:
              {
                // We use a recursive call because we also need to
                // check superinterfaces.
-               if (is_assignable_from_slow (target, source->interfaces[i]))
+               if (is_assignable_from_slow (target, source->getInterface (i)))
                  return true;
              }
          }
@@ -579,9 +583,11 @@ private:
     //
     // First, when constructing a new object, it is the PC of the
     // `new' instruction which created the object.  We use the special
-    // value UNINIT to mean that this is uninitialized, and the
-    // special value SELF for the case where the current method is
-    // itself the <init> method.
+    // value UNINIT to mean that this is uninitialized.  The special
+    // value SELF is used for the case where the current method is
+    // itself the <init> method.  the special value EITHER is used
+    // when we may optionally allow either an uninitialized or
+    // initialized reference to match.
     //
     // Second, when the key is return_address_type, this holds the PC
     // of the instruction following the `jsr'.
@@ -589,6 +595,7 @@ private:
 
     static const int UNINIT = -2;
     static const int SELF = -1;
+    static const int EITHER = -3;
 
     // Basic constructor.
     type ()
@@ -735,23 +742,51 @@ private:
       if (k.klass == NULL)
        verifier->verify_fail ("programmer error in type::compatible");
 
-      // An initialized type and an uninitialized type are not
-      // compatible.
-      if (isinitialized () != k.isinitialized ())
-       return false;
-
-      // Two uninitialized objects are compatible if either:
-      // * The PCs are identical, or
-      // * One PC is UNINIT.
-      if (! isinitialized ())
+      // Handle the special 'EITHER' case, which is only used in a
+      // special case of 'putfield'.  Note that we only need to handle
+      // this on the LHS of a check.
+      if (! isinitialized () && pc == EITHER)
        {
-         if (pc != k.pc && pc != UNINIT && k.pc != UNINIT)
+         // If the RHS is uninitialized, it must be an uninitialized
+         // 'this'.
+         if (! k.isinitialized () && k.pc != SELF)
            return false;
        }
+      else if (isinitialized () != k.isinitialized ())
+       {
+         // An initialized type and an uninitialized type are not
+         // otherwise compatible.
+         return false;
+       }
+      else
+       {
+         // Two uninitialized objects are compatible if either:
+         // * The PCs are identical, or
+         // * One PC is UNINIT.
+         if (! isinitialized ())
+           {
+             if (pc != k.pc && pc != UNINIT && k.pc != UNINIT)
+               return false;
+           }
+       }
 
       return klass->compatible(k.klass, verifier);
     }
 
+    bool equals (const type &other, _Jv_BytecodeVerifier *vfy)
+    {
+      // Only works for reference types.
+      if ((key != reference_type
+          && key != uninitialized_reference_type)
+         || (other.key != reference_type
+             && other.key != uninitialized_reference_type))
+       return false;
+      // Only for single-valued types.
+      if (klass->ref_next || other.klass->ref_next)
+       return false;
+      return klass->equals (other.klass, vfy);
+    }
+
     bool isvoid () const
     {
       return key == void_type;
@@ -1101,28 +1136,6 @@ private:
       return changed;
     }
 
-    // Throw an exception if there is an uninitialized object on the
-    // stack or in a local variable.  EXCEPTION_SEMANTICS controls
-    // whether we're using backwards-branch or exception-handing
-    // semantics.
-    void check_no_uninitialized_objects (int max_locals,
-                                        _Jv_BytecodeVerifier *verifier,
-                                        bool exception_semantics = false)
-    {
-      if (! exception_semantics)
-       {
-         for (int i = 0; i < stacktop; ++i)
-           if (stack[i].isreference () && ! stack[i].isinitialized ())
-             verifier->verify_fail ("uninitialized object on stack");
-       }
-
-      for (int i = 0; i < max_locals; ++i)
-       if (locals[i].isreference () && ! locals[i].isinitialized ())
-         verifier->verify_fail ("uninitialized object in local variable");
-
-      check_this_initialized (verifier);
-    }
-
     // Ensure that `this' has been initialized.
     void check_this_initialized (_Jv_BytecodeVerifier *verifier)
     {
@@ -1437,15 +1450,19 @@ private:
   void push_jump (int offset)
   {
     int npc = compute_jump (offset);
-    if (npc < PC)
-      current_state->check_no_uninitialized_objects (current_method->max_locals, this);
+    // According to the JVM Spec, we need to check for uninitialized
+    // objects here.  However, this does not actually affect type
+    // safety, and the Eclipse java compiler generates code that
+    // violates this constraint.
     merge_into (npc, current_state);
   }
 
   void push_exception_jump (type t, int pc)
   {
-    current_state->check_no_uninitialized_objects (current_method->max_locals,
-                                                  this, true);
+    // According to the JVM Spec, we need to check for uninitialized
+    // objects here.  However, this does not actually affect type
+    // safety, and the Eclipse java compiler generates code that
+    // violates this constraint.
     state s (current_state, current_method->max_stack,
             current_method->max_locals);
     if (current_method->max_stack < 1)
@@ -1507,9 +1524,10 @@ private:
     if (npc >= current_method->code_length)
       verify_fail ("fell off end");
 
-    if (npc < PC)
-      current_state->check_no_uninitialized_objects (current_method->max_locals,
-                                                    this);
+    // According to the JVM Spec, we need to check for uninitialized
+    // objects here.  However, this does not actually affect type
+    // safety, and the Eclipse java compiler generates code that
+    // violates this constraint.
     merge_into (npc, current_state);
     invalidate_pc ();
   }
@@ -1518,8 +1536,10 @@ private:
   {
     int npc = compute_jump (offset);
 
-    if (npc < PC)
-      current_state->check_no_uninitialized_objects (current_method->max_locals, this);
+    // According to the JVM Spec, we need to check for uninitialized
+    // objects here.  However, this does not actually affect type
+    // safety, and the Eclipse java compiler generates code that
+    // violates this constraint.
 
     // Modify our state as appropriate for entry into a subroutine.
     type ret_addr (return_address_type);
@@ -1979,7 +1999,9 @@ private:
   }
 
   // Return field's type, compute class' type if requested.
-  type check_field_constant (int index, type *class_type = NULL)
+  // If PUTFIELD is true, use the special 'putfield' semantics.
+  type check_field_constant (int index, type *class_type = NULL,
+                            bool putfield = false)
   {
     _Jv_Utf8Const *name, *field_type;
     type ct = handle_field_or_method (index,
@@ -1987,9 +2009,29 @@ private:
                                      &name, &field_type);
     if (class_type)
       *class_type = ct;
-    if (field_type->data[0] == '[' || field_type->data[0] == 'L')
-      return type (field_type, this);
-    return get_type_val_for_signature (field_type->data[0]);
+    type result;
+    if (field_type->first() == '[' || field_type->first() == 'L')
+      result = type (field_type, this);
+    else
+      result = get_type_val_for_signature (field_type->first());
+
+    // We have an obscure special case here: we can use `putfield' on
+    // a field declared in this class, even if `this' has not yet been
+    // initialized.
+    if (putfield
+       && ! current_state->this_type.isinitialized ()
+       && current_state->this_type.pc == type::SELF
+       && current_state->this_type.equals (ct, this)
+       // We don't look at the signature, figuring that if it is
+       // wrong we will fail during linking.  FIXME?
+       && _Jv_Linker::has_field_p (current_class, name))
+      // Note that we don't actually know whether we're going to match
+      // against 'this' or some other object of the same type.  So,
+      // here we set things up so that it doesn't matter.  This relies
+      // on knowing what our caller is up to.
+      class_type->set_uninitialized (type::EITHER, this);
+
+    return result;
   }
 
   type check_method_constant (int index, bool is_interface,
@@ -2045,7 +2087,8 @@ private:
   void compute_argument_types (_Jv_Utf8Const *signature,
                               type *types)
   {
-    char *p = signature->data;
+    char *p = signature->chars();
+
     // Skip `('.
     ++p;
 
@@ -2056,7 +2099,7 @@ private:
 
   type compute_return_type (_Jv_Utf8Const *signature)
   {
-    char *p = signature->data;
+    char *p = signature->chars();
     while (*p != ')')
       ++p;
     ++p;
@@ -2798,15 +2841,8 @@ private:
          case op_putfield:
            {
              type klass;
-             type field = check_field_constant (get_ushort (), &klass);
+             type field = check_field_constant (get_ushort (), &klass, true);
              pop_type (field);
-
-             // We have an obscure special case here: we can use
-             // `putfield' on a field declared in this class, even if
-             // `this' has not yet been initialized.
-             if (! current_state->this_type.isinitialized ()
-                 && current_state->this_type.pc == type::SELF)
-               klass.set_uninitialized (type::SELF, this);
              pop_type (klass);
            }
            break;
@@ -2840,7 +2876,7 @@ private:
                  if (opcode != op_invokespecial)
                    verify_fail ("can't invoke <init>");
                }
-             else if (method_name->data[0] == '<')
+             else if (method_name->first() == '<')
                verify_fail ("can't invoke method starting with `<'");
 
              // Pop arguments and check types.
@@ -3055,7 +3091,7 @@ public:
     // We just print the text as utf-8.  This is just for debugging
     // anyway.
     debug_print ("--------------------------------\n");
-    debug_print ("-- Verifying method `%s'\n", m->self->name->data);
+    debug_print ("-- Verifying method `%s'\n", m->self->name->chars());
 
     current_method = m;
     bytecode = m->bytecode ();
@@ -3076,7 +3112,6 @@ public:
     while (utf8_list != NULL)
       {
        linked<_Jv_Utf8Const> *n = utf8_list->next;
-       _Jv_Free (utf8_list->val);
        _Jv_Free (utf8_list);
        utf8_list = n;
       }