OSDN Git Service

* config/bfin/bfin.c (emit_link_insn, effective_address_32bit_p,
[pf3gnuchains/gcc-fork.git] / libjava / link.cc
index 46d8586..0a70573 100644 (file)
@@ -1,6 +1,6 @@
 // link.cc - Code for linking and resolving classes and pool entries.
 
-/* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation
+/* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation
 
    This file is part of libgcj.
 
@@ -13,6 +13,8 @@ details.  */
 #include <config.h>
 #include <platform.h>
 
+#include <stdio.h>
+
 #include <java-interp.h>
 
 #include <jvm.h>
@@ -88,12 +90,139 @@ _Jv_Linker::resolve_field (_Jv_Field *field, java::lang::ClassLoader *loader)
 {
   if (! field->isResolved ())
     {
-      _Jv_Utf8Const *sig = (_Jv_Utf8Const*)field->type;
-      field->type = _Jv_FindClassFromSignature (sig->chars(), loader);
+      _Jv_Utf8Const *sig = (_Jv_Utf8Const *) field->type;
+      jclass type = _Jv_FindClassFromSignature (sig->chars(), loader);
+      if (type == NULL)
+       throw new java::lang::NoClassDefFoundError(field->name->toString());
+      field->type = type;
       field->flags &= ~_Jv_FIELD_UNRESOLVED_FLAG;
     }
 }
 
+// A helper for find_field that knows how to recursively search
+// superclasses and interfaces.
+_Jv_Field *
+_Jv_Linker::find_field_helper (jclass search, _Jv_Utf8Const *name,
+                              _Jv_Utf8Const *type_name,
+                              jclass *declarer)
+{
+  while (search)
+    {
+      // From 5.4.3.2.  First search class itself.
+      for (int i = 0; i < search->field_count; ++i)
+       {
+         _Jv_Field *field = &search->fields[i];
+         if (! _Jv_equalUtf8Consts (field->name, name))
+           continue;
+
+         if (! field->isResolved ())
+           resolve_field (field, search->loader);
+
+         // Note that we compare type names and not types.  This is
+         // bizarre, but we do it because we want to find a field
+         // (and terminate the search) if it has the correct
+         // descriptor -- but then later reject it if the class
+         // loader check results in different classes.  We can't just
+         // pass in the descriptor and check that way, because when
+         // the field is already resolved there is no easy way to
+         // find its descriptor again.
+         if (_Jv_equalUtf8Consts (type_name, field->type->name))
+           {
+             *declarer = search;
+             return field;
+           }
+       }
+
+      // Next search direct interfaces.
+      for (int i = 0; i < search->interface_count; ++i)
+       {
+         _Jv_Field *result = find_field_helper (search->interfaces[i], name,
+                                                type_name, declarer);
+         if (result)
+           return result;
+       }
+
+      // Now search superclass.
+      search = search->superclass;
+    }
+
+  return NULL;
+}
+
+bool
+_Jv_Linker::has_field_p (jclass search, _Jv_Utf8Const *field_name)
+{
+  for (int i = 0; i < search->field_count; ++i)
+    {
+      _Jv_Field *field = &search->fields[i];
+      if (_Jv_equalUtf8Consts (field->name, field_name))
+       return true;
+    }
+  return false;
+}
+
+// Find a field.
+// KLASS is the class that is requesting the field.
+// OWNER is the class in which the field should be found.
+// FIELD_TYPE_NAME is the type descriptor for the field.
+// Fill FOUND_CLASS with the address of the class in which the field
+// is actually declared.
+// This function does the class loader type checks, and
+// also access checks.  Returns the field, or throws an
+// exception on error.
+_Jv_Field *
+_Jv_Linker::find_field (jclass klass, jclass owner,
+                       jclass *found_class,
+                       _Jv_Utf8Const *field_name,
+                       _Jv_Utf8Const *field_type_name)
+{
+  // FIXME: this allocates a _Jv_Utf8Const each time.  We should make
+  // it cheaper.
+  jclass field_type = _Jv_FindClassFromSignature (field_type_name->chars(),
+                                                 klass->loader);
+  if (field_type == NULL)
+    throw new java::lang::NoClassDefFoundError(field_name->toString());
+
+  _Jv_Field *the_field = find_field_helper (owner, field_name,
+                                           field_type->name, found_class);
+
+  if (the_field == 0)
+    {
+      java::lang::StringBuffer *sb = new java::lang::StringBuffer();
+      sb->append(JvNewStringLatin1("field "));
+      sb->append(owner->getName());
+      sb->append(JvNewStringLatin1("."));
+      sb->append(_Jv_NewStringUTF(field_name->chars()));
+      sb->append(JvNewStringLatin1(" was not found."));
+      throw new java::lang::NoSuchFieldError (sb->toString());
+    }
+
+  if (_Jv_CheckAccess (klass, *found_class, the_field->flags))
+    {
+      // Note that the field returned by find_field_helper is always
+      // resolved.  There's no point checking class loaders here,
+      // since we already did the work to look up all the types.
+      // FIXME: being lazy here would be nice.
+      if (the_field->type != field_type)
+       throw new java::lang::LinkageError
+         (JvNewStringLatin1 
+          ("field type mismatch with different loaders"));
+    }
+  else
+    {
+      java::lang::StringBuffer *sb
+       = new java::lang::StringBuffer ();
+      sb->append(klass->getName());
+      sb->append(JvNewStringLatin1(": "));
+      sb->append((*found_class)->getName());
+      sb->append(JvNewStringLatin1("."));
+      sb->append(_Jv_NewStringUtf8Const (field_name));
+      throw new java::lang::IllegalAccessError(sb->toString());
+    }
+
+  return the_field;
+}
+
 _Jv_word
 _Jv_Linker::resolve_pool_entry (jclass klass, int index)
 {
@@ -173,73 +302,13 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index)
        _Jv_Utf8Const *field_name = pool->data[name_index].utf8;
        _Jv_Utf8Const *field_type_name = pool->data[type_index].utf8;
 
-       // FIXME: The implementation of this function
-       // (_Jv_FindClassFromSignature) will generate an instance of
-       // _Jv_Utf8Const for each call if the field type is a class name
-       // (Lxx.yy.Z;).  This may be too expensive to do for each and
-       // every fieldref being resolved.  For now, we fix the problem by
-       // only doing it when we have a loader different from the class
-       // declaring the field.
-
-       jclass field_type = 0;
-
-       if (owner->loader != klass->loader)
-         field_type = _Jv_FindClassFromSignature (field_type_name->chars(),
-                                                  klass->loader);
-      
-       _Jv_Field* the_field = 0;
-
-       for (jclass cls = owner; cls != 0; cls = cls->getSuperclass ())
-         {
-           for (int i = 0;  i < cls->field_count;  i++)
-             {
-               _Jv_Field *field = &cls->fields[i];
-               if (! _Jv_equalUtf8Consts (field->name, field_name))
-                 continue;
-
-               if (_Jv_CheckAccess (klass, cls, field->flags))
-                 {
-                   // Resolve the field using the class' own loader if
-                   // necessary.
-
-                   if (!field->isResolved ())
-                     resolve_field (field, cls->loader);
-
-                   if (field_type != 0 && field->type != field_type)
-                     throw new java::lang::LinkageError
-                       (JvNewStringLatin1 
-                        ("field type mismatch with different loaders"));
-
-                   the_field = field;
-                   goto end_of_field_search;
-                 }
-               else
-                 {
-                   java::lang::StringBuffer *sb
-                     = new java::lang::StringBuffer ();
-                   sb->append(klass->getName());
-                   sb->append(JvNewStringLatin1(": "));
-                   sb->append(cls->getName());
-                   sb->append(JvNewStringLatin1("."));
-                   sb->append(_Jv_NewStringUtf8Const (field_name));
-                   throw new java::lang::IllegalAccessError(sb->toString());
-                 }
-             }
-         }
-
-      end_of_field_search:
-       if (the_field == 0)
-         {
-           java::lang::StringBuffer *sb = new java::lang::StringBuffer();
-           sb->append(JvNewStringLatin1("field "));
-           sb->append(owner->getName());
-           sb->append(JvNewStringLatin1("."));
-           sb->append(_Jv_NewStringUTF(field_name->chars()));
-           sb->append(JvNewStringLatin1(" was not found."));
-           throw
-             new java::lang::IncompatibleClassChangeError (sb->toString());
-         }
-
+       jclass found_class = 0;
+       _Jv_Field *the_field = find_field (klass, owner, 
+                                          &found_class,
+                                          field_name,
+                                          field_type_name);
+       if (owner != found_class)
+         _Jv_InitClass (found_class);
        pool->data[index].field = the_field;
        pool->tags[index] |= JV_CONSTANT_ResolvedFlag;
       }
@@ -296,7 +365,7 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index)
            ifaces.list = (jclass *) _Jv_Malloc (ifaces.len
                                                 * sizeof (jclass *));
 
-           get_interfaces (owner, &ifaces);      
+           get_interfaces (owner, &ifaces);
 
            for (int i = 0; i < ifaces.count; i++)
              {
@@ -624,12 +693,21 @@ _Jv_Linker::generate_itable (jclass klass, _Jv_ifaces *ifaces,
 
 // Format method name for use in error messages.
 jstring
-_Jv_GetMethodString (jclass klass, _Jv_Utf8Const *name)
+_Jv_GetMethodString (jclass klass, _Jv_Method *meth,
+                    jclass derived)
 {
-  jstring r = klass->name->toString();
-  r = r->concat (JvNewStringUTF ("."));
-  r = r->concat (name->toString());
-  return r;
+  using namespace java::lang;
+  StringBuffer *buf = new StringBuffer (klass->name->toString());
+  buf->append (jchar ('.'));
+  buf->append (meth->name->toString());
+  buf->append ((jchar) ' ');
+  buf->append (meth->signature->toString());
+  if (derived)
+    {
+      buf->append(JvNewStringLatin1(" in "));
+      buf->append(derived->name->toString());
+    }
+  return buf->toString();
 }
 
 void 
@@ -638,6 +716,13 @@ _Jv_ThrowNoSuchMethodError ()
   throw new java::lang::NoSuchMethodError;
 }
 
+// This is put in empty vtable slots.
+static void
+_Jv_abstractMethodError (void)
+{
+  throw new java::lang::AbstractMethodError();
+}
+
 // Each superinterface of a class (i.e. each interface that the class
 // directly or indirectly implements) has a corresponding "Partial
 // Interface Dispatch Table" whose size is (number of methods + 1) words.
@@ -679,15 +764,15 @@ _Jv_Linker::append_partial_itable (jclass klass, jclass iface,
         {
          if ((meth->accflags & Modifier::STATIC) != 0)
            throw new java::lang::IncompatibleClassChangeError
-             (_Jv_GetMethodString (klass, meth->name));
-         if ((meth->accflags & Modifier::ABSTRACT) != 0)
-           throw new java::lang::AbstractMethodError
-             (_Jv_GetMethodString (klass, meth->name));
+             (_Jv_GetMethodString (klass, meth));
          if ((meth->accflags & Modifier::PUBLIC) == 0)
            throw new java::lang::IllegalAccessError
-             (_Jv_GetMethodString (klass, meth->name));
+             (_Jv_GetMethodString (klass, meth));
 
-         itable[pos] = meth->ncode;
+         if ((meth->accflags & Modifier::ABSTRACT) != 0)
+           itable[pos] = (void *) &_Jv_abstractMethodError;
+         else
+           itable[pos] = meth->ncode;
        }
       else
         {
@@ -885,55 +970,16 @@ _Jv_Linker::link_symbol_table (jclass klass)
          continue;
        }
 
-      // try fields
+      // Try fields.
       {
-       _Jv_Field *the_field = NULL;
-
        wait_for_state(target_class, JV_STATE_PREPARED);
-       for (jclass cls = target_class; cls != 0; cls = cls->getSuperclass ())
-         {
-           for (int i = 0; i < cls->field_count; i++)
-             {
-               _Jv_Field *field = &cls->fields[i];
-               if (! _Jv_equalUtf8Consts (field->name, sym.name))
-                 continue;
-
-               // FIXME: What access checks should we perform here?
-//             if (_Jv_CheckAccess (klass, cls, field->flags))
-//               {
-
-               if (!field->isResolved ())
-                 resolve_field (field, cls->loader);
-
-//             if (field_type != 0 && field->type != field_type)
-//               throw new java::lang::LinkageError
-//                 (JvNewStringLatin1 
-//                  ("field type mismatch with different loaders"));
-
-               the_field = field;
-               if (debug_link)
-                 fprintf (stderr, "  offsets[%d] = %d (class %s@%p : %s)\n",
-                          (int)index,
-                          (int)field->u.boffset,
-                          (const char*)cls->name->chars(),
-                          cls,
-                          (const char*)field->name->chars());
-               goto end_of_field_search;
-             }
-         }
-      end_of_field_search:
-       if (the_field != NULL)
-         {
-           if ((the_field->flags & java::lang::reflect::Modifier::STATIC))
-             throw new java::lang::IncompatibleClassChangeError;
-           else
-             klass->otable->offsets[index] = the_field->u.boffset;
-         }
+       jclass found_class;
+       _Jv_Field *the_field = find_field (klass, target_class, &found_class,
+                                          sym.name, sym.signature);
+       if ((the_field->flags & java::lang::reflect::Modifier::STATIC))
+         throw new java::lang::IncompatibleClassChangeError;
        else
-         {
-           throw new java::lang::NoSuchFieldError
-             (_Jv_NewStringUtf8Const (sym.name));
-         }
+         klass->otable->offsets[index] = the_field->u.boffset;
       }
     }
 
@@ -1005,48 +1051,16 @@ _Jv_Linker::link_symbol_table (jclass klass)
          continue;
        }
 
-      // try fields
+      // Try fields.
       {
-       _Jv_Field *the_field = NULL;
-
        wait_for_state(target_class, JV_STATE_PREPARED);
-       for (jclass cls = target_class; cls != 0; cls = cls->getSuperclass ())
-         {
-           for (int i = 0; i < cls->field_count; i++)
-             {
-               _Jv_Field *field = &cls->fields[i];
-               if (! _Jv_equalUtf8Consts (field->name, sym.name))
-                 continue;
-
-               // FIXME: What access checks should we perform here?
-//             if (_Jv_CheckAccess (klass, cls, field->flags))
-//               {
-
-               if (!field->isResolved ())
-                 resolve_field (field, cls->loader);
-
-//             if (field_type != 0 && field->type != field_type)
-//               throw new java::lang::LinkageError
-//                 (JvNewStringLatin1 
-//                  ("field type mismatch with different loaders"));
-
-               the_field = field;
-               goto end_of_static_field_search;
-             }
-         }
-      end_of_static_field_search:
-       if (the_field != NULL)
-         {
-           if ((the_field->flags & java::lang::reflect::Modifier::STATIC))
-             klass->atable->addresses[index] = the_field->u.addr;
-           else
-             throw new java::lang::IncompatibleClassChangeError;
-         }
+       jclass found_class;
+       _Jv_Field *the_field = find_field (klass, target_class, &found_class,
+                                          sym.name, sym.signature);
+       if ((the_field->flags & java::lang::reflect::Modifier::STATIC))
+         klass->atable->addresses[index] = the_field->u.addr;
        else
-         {
-           throw new java::lang::NoSuchFieldError
-             (_Jv_NewStringUtf8Const (sym.name));
-         }
+         throw new java::lang::IncompatibleClassChangeError;
       }
     }
 
@@ -1124,13 +1138,6 @@ _Jv_Linker::link_exception_table (jclass self)
   self->catch_classes->classname = (_Jv_Utf8Const *)-1;
 }
   
-// This is put in empty vtable slots.
-static void
-_Jv_abstractMethodError (void)
-{
-  throw new java::lang::AbstractMethodError();
-}
-
 // Set itable method indexes for members of interface IFACE.
 void
 _Jv_Linker::layout_interface_methods (jclass iface)
@@ -1193,9 +1200,9 @@ _Jv_Linker::layout_vtable_methods (jclass klass)
                  using namespace java::lang;
                  StringBuffer *sb = new StringBuffer();
                  sb->append(JvNewStringLatin1("method "));
-                 sb->append(_Jv_GetMethodString(klass, meth->name));
+                 sb->append(_Jv_GetMethodString(klass, meth));
                  sb->append(JvNewStringLatin1(" overrides final method "));
-                 sb->append(_Jv_GetMethodString(declarer, super_meth->name));
+                 sb->append(_Jv_GetMethodString(declarer, super_meth));
                  throw new VerifyError(sb->toString());
                }
            }
@@ -1222,6 +1229,8 @@ _Jv_Linker::set_vtable_entries (jclass klass, _Jv_VTable *vtable)
       if (meth->index == (_Jv_ushort) -1)
        continue;
       if ((meth->accflags & Modifier::ABSTRACT))
+       // FIXME: it might be nice to have a libffi trampoline here,
+       // so we could pass in the method name and other information.
        vtable->set_method(meth->index, (void *) &_Jv_abstractMethodError);
       else
        vtable->set_method(meth->index, meth->ncode);
@@ -1270,33 +1279,9 @@ _Jv_Linker::make_vtable (jclass klass)
   // override an old one.
   set_vtable_entries (klass, vtable);
 
-  // It is an error to have an abstract method in a concrete class.
-  if (! (klass->accflags & Modifier::ABSTRACT))
-    {
-      for (int i = 0; i < klass->vtable_method_count; ++i)
-       if (vtable->get_method(i) == (void *) &_Jv_abstractMethodError)
-         {
-           using namespace java::lang;
-           while (klass != NULL)
-             {
-               for (int j = 0; j < klass->method_count; ++j)
-                 {
-                   if (klass->methods[j].index == i)
-                     {
-                       StringBuffer *buf = new StringBuffer ();
-                       buf->append (_Jv_NewStringUtf8Const (klass->methods[j].name));
-                       buf->append ((jchar) ' ');
-                       buf->append (_Jv_NewStringUtf8Const (klass->methods[j].signature));
-                       throw new AbstractMethodError (buf->toString ());
-                     }
-                 }
-               klass = klass->getSuperclass ();
-             }
-           // Couldn't find the name, which is weird.
-           // But we still must throw the error.
-           throw new AbstractMethodError ();
-         }
-    }
+  // Note that we don't check for abstract methods here.  We used to,
+  // but there is a JVMS clarification that indicates that a check
+  // here would be too eager.  And, a simple test case confirms this.
 }
 
 // Lay out the class, allocating space for static fields and computing
@@ -1423,9 +1408,7 @@ _Jv_Linker::ensure_class_linked (jclass klass)
       // a reference to a class we can't access.  This can validly
       // occur in an obscure case involving the InnerClasses
       // attribute.
-#ifdef INTERPRETER
       if (! _Jv_IsInterpretedClass (klass))
-#endif
        {
          // Resolve class constants first, since other constant pool
          // entries may rely on these.
@@ -1680,11 +1663,7 @@ _Jv_Linker::print_class_loaded (jclass klass)
 
   // We use a somewhat bogus test for the ABI here.
   char *abi;
-#ifdef INTERPRETER
   if (_Jv_IsInterpretedClass (klass))
-#else
-  if (false)
-#endif
     abi = "bytecode";
   else if (klass->state == JV_STATE_PRELOADING)
     abi = "BC-compiled";
@@ -1721,10 +1700,7 @@ _Jv_Linker::wait_for_state (jclass klass, int state)
   if (gcj::verbose_class_flag
       && (klass->state == JV_STATE_COMPILED
          || klass->state == JV_STATE_PRELOADING)
-#ifdef INTERPRETER
-      && ! _Jv_IsInterpretedClass (klass)
-#endif
-      )
+      && ! _Jv_IsInterpretedClass (klass))
     print_class_loaded (klass);
 
   try