OSDN Git Service

2008-08-22 Andrew Haley <aph@redhat.com>
[pf3gnuchains/gcc-fork.git] / libjava / link.cc
index 7f39888..c07b6e1 100644 (file)
@@ -1,6 +1,7 @@
 // link.cc - Code for linking and resolving classes and pool entries.
 
-/* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation
+/* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+   Free Software Foundation
 
    This file is part of libgcj.
 
@@ -33,6 +34,10 @@ details.  */
 #include <limits.h>
 #include <java-cpool.h>
 #include <execution.h>
+#ifdef INTERPRETER
+#include <jvmti.h>
+#include "jvmti-int.h"
+#endif
 #include <java/lang/Class.h>
 #include <java/lang/String.h>
 #include <java/lang/StringBuffer.h>
@@ -241,13 +246,9 @@ _Jv_Linker::find_field (jclass klass, jclass owner,
   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"));
+      // resolved.  However, we still use the constraint mechanism
+      // because this may affect other lookups.
+      _Jv_CheckOrCreateLoadingConstraint (klass, (*found_class)->loader);
     }
   else
     {
@@ -264,6 +265,23 @@ _Jv_Linker::find_field (jclass klass, jclass owner,
   return the_field;
 }
 
+// Check loading constraints for method.
+void
+_Jv_Linker::check_loading_constraints (_Jv_Method *method, jclass self_class,
+                                      jclass other_class)
+{
+  JArray<jclass> *klass_args;
+  jclass klass_return;
+
+  _Jv_GetTypesFromSignature (method, self_class, &klass_args, &klass_return);
+  jclass *klass_arg = elements (klass_args);
+  java::lang::ClassLoader *found_loader = other_class->loader;
+
+  _Jv_CheckOrCreateLoadingConstraint (klass_return, found_loader);
+  for (int i = 0; i < klass_args->length; i++)
+    _Jv_CheckOrCreateLoadingConstraint (*(klass_arg++), found_loader);
+}
+
 _Jv_Method *
 _Jv_Linker::resolve_method_entry (jclass klass, jclass &found_class,
                                  int class_index, int name_and_type_index,
@@ -341,14 +359,6 @@ _Jv_Linker::resolve_method_entry (jclass klass, jclass &found_class,
   
 
  end_of_method_search:
-
-  // FIXME: if (cls->loader != klass->loader), then we
-  // must actually check that the types of arguments
-  // correspond.  That is, for each argument type, and
-  // the return type, doing _Jv_FindClassFromSignature
-  // with either loader should produce the same result,
-  // i.e., exactly the same jclass object. JVMS 5.4.3.3    
-    
   if (the_method == 0)
     {
       java::lang::StringBuffer *sb = new java::lang::StringBuffer();
@@ -362,9 +372,27 @@ _Jv_Linker::resolve_method_entry (jclass klass, jclass &found_class,
       throw new java::lang::NoSuchMethodError (sb->toString());
     }
 
+  // if (found_class->loader != klass->loader), then we must actually
+  // check that the types of arguments correspond.  JVMS 5.4.3.3.
+  if (found_class->loader != klass->loader)
+    check_loading_constraints (the_method, klass, found_class);
+  
   return the_method;
 }
 
+_Jv_Mutex_t _Jv_Linker::resolve_mutex;
+
+void
+_Jv_Linker::init (void)
+{
+  _Jv_MutexInit (&_Jv_Linker::resolve_mutex);
+}
+
+// Locking in resolve_pool_entry is somewhat subtle.  Constant
+// resolution is idempotent, so it doesn't matter if two threads
+// resolve the same entry.  However, it is important that we always
+// write the resolved flag and the data together, atomically.  It is
+// also important that we read them atomically.
 _Jv_word
 _Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy)
 {
@@ -372,6 +400,10 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy)
 
   if (GC_base (klass) && klass->constants.data
       && ! GC_base (klass->constants.data))
+    // If a class is heap-allocated but the constant pool is not this
+    // is a "new ABI" class, i.e. one where the initial constant pool
+    // is in the read-only data section of an object file.  Copy the
+    // initial constant pool from there to a new heap-allocated pool.
     {
       jsize count = klass->constants.size;
       if (count)
@@ -387,14 +419,18 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy)
 
   _Jv_Constants *pool = &klass->constants;
 
-  if ((pool->tags[index] & JV_CONSTANT_ResolvedFlag) != 0)
-    return pool->data[index];
+  jbyte tags;
+  _Jv_word data;
+  tags = read_cpool_entry (&data, pool, index);
+
+  if ((tags & JV_CONSTANT_ResolvedFlag) != 0)
+    return data;
 
-  switch (pool->tags[index] & ~JV_CONSTANT_LazyFlag)
+  switch (tags & ~JV_CONSTANT_LazyFlag)
     {
     case JV_CONSTANT_Class:
       {
-       _Jv_Utf8Const *name = pool->data[index].utf8;
+       _Jv_Utf8Const *name = data.utf8;
 
        jclass found;
        if (name->first() == '[')
@@ -413,8 +449,8 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy)
              {
                found = _Jv_NewClass(name, NULL, NULL);
                found->state = JV_STATE_PHANTOM;
-               pool->tags[index] |= JV_CONSTANT_ResolvedFlag;
-               pool->data[index].clazz = found;
+               tags |= JV_CONSTANT_ResolvedFlag;
+               data.clazz = found;
                break;
              }
            else
@@ -432,8 +468,8 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy)
            || (_Jv_ClassNameSamePackage (check->name,
                                          klass->name)))
          {
-           pool->data[index].clazz = found;
-           pool->tags[index] |= JV_CONSTANT_ResolvedFlag;
+           data.clazz = found;
+           tags |= JV_CONSTANT_ResolvedFlag;
          }
        else
          {
@@ -449,16 +485,16 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy)
     case JV_CONSTANT_String:
       {
        jstring str;
-       str = _Jv_NewStringUtf8Const (pool->data[index].utf8);
-       pool->data[index].o = str;
-       pool->tags[index] |= JV_CONSTANT_ResolvedFlag;
+       str = _Jv_NewStringUtf8Const (data.utf8);
+       data.o = str;
+       tags |= JV_CONSTANT_ResolvedFlag;
       }
       break;
 
     case JV_CONSTANT_Fieldref:
       {
        _Jv_ushort class_index, name_and_type_index;
-       _Jv_loadIndexes (&pool->data[index],
+       _Jv_loadIndexes (&data,
                         class_index,
                         name_and_type_index);
        jclass owner = (resolve_pool_entry (klass, class_index, true)).clazz;
@@ -488,8 +524,8 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy)
        // Initialize the field's declaring class, not its qualifying
        // class.
        _Jv_InitClass (found_class);
-       pool->data[index].field = the_field;
-       pool->tags[index] |= JV_CONSTANT_ResolvedFlag;
+       data.field = the_field;
+       tags |= JV_CONSTANT_ResolvedFlag;
       }
       break;
 
@@ -497,7 +533,7 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy)
     case JV_CONSTANT_InterfaceMethodref:
       {
        _Jv_ushort class_index, name_and_type_index;
-       _Jv_loadIndexes (&pool->data[index],
+       _Jv_loadIndexes (&data,
                         class_index,
                         name_and_type_index);
 
@@ -506,18 +542,21 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy)
        the_method = resolve_method_entry (klass, found_class,
                                           class_index, name_and_type_index,
                                           true,
-                                          pool->tags[index] == JV_CONSTANT_InterfaceMethodref);
+                                          tags == JV_CONSTANT_InterfaceMethodref);
       
-       pool->data[index].rmethod
+       data.rmethod
          = klass->engine->resolve_method(the_method,
                                          found_class,
                                          ((the_method->accflags
                                            & Modifier::STATIC) != 0));
-       pool->tags[index] |= JV_CONSTANT_ResolvedFlag;
+       tags |= JV_CONSTANT_ResolvedFlag;
       }
       break;
     }
-  return pool->data[index];
+
+  write_cpool_entry (data, tags, pool, index);
+
+  return data;
 }
 
 // This function is used to lazily locate superclasses and
@@ -632,10 +671,11 @@ _Jv_Linker::prepare_constant_time_tables (jclass klass)
   // interfaces or primitive types.
    
   jclass klass0 = klass;
-  jboolean has_interfaces = 0;
+  jboolean has_interfaces = false;
   while (klass0 != &java::lang::Object::class$)
     {
-      has_interfaces += klass0->interface_count;
+      if (klass0->interface_count)
+       has_interfaces = true;
       klass0 = klass0->superclass;
       klass->depth++;
     }
@@ -825,7 +865,7 @@ _Jv_ThrowNoSuchMethodError ()
   throw new java::lang::NoSuchMethodError;
 }
 
-#if defined USE_LIBFFI && FFI_CLOSURES
+#if defined USE_LIBFFI && FFI_CLOSURES && defined(INTERPRETER)
 // A function whose invocation is prepared using libffi. It gets called
 // whenever a static method of a missing class is invoked. The data argument
 // holds a reference to a String denoting the missing class.
@@ -893,7 +933,8 @@ _Jv_Linker::append_partial_itable (jclass klass, jclass iface,
        continue;
 
       meth = NULL;
-      for (jclass cl = klass; cl; cl = cl->getSuperclass())
+      jclass cl;
+      for (cl = klass; cl; cl = cl->getSuperclass())
         {
          meth = _Jv_GetMethodLocal (cl, iface->methods[j].name,
                                     iface->methods[j].signature);
@@ -915,6 +956,9 @@ _Jv_Linker::append_partial_itable (jclass klass, jclass iface,
            itable[pos] = (void *) &_Jv_ThrowAbstractMethodError;
          else
            itable[pos] = meth->ncode;
+
+         if (cl->loader != iface->loader)
+           check_loading_constraints (meth, cl, iface);
        }
       else
         {
@@ -1011,7 +1055,7 @@ _Jv_Linker::find_iindex (jclass *ifaces, jshort *offsets, jshort num)
   return i;
 }
 
-#if defined USE_LIBFFI && FFI_CLOSURES
+#if defined USE_LIBFFI && FFI_CLOSURES && defined(INTERPRETER)
 // We use a structure of this type to store the closure that
 // represents a missing method.
 struct method_closure
@@ -1020,15 +1064,17 @@ struct method_closure
   // be the same as the address of the overall structure.  This is due
   // to disabling interior pointers in the GC.
   ffi_closure closure;
+  _Jv_ClosureList list;
   ffi_cif cif;
   ffi_type *arg_types[1];
 };
 
 void *
-_Jv_Linker::create_error_method (_Jv_Utf8Const *class_name)
+_Jv_Linker::create_error_method (_Jv_Utf8Const *class_name, jclass klass)
 {
+  void *code;
   method_closure *closure
-    = (method_closure *) _Jv_AllocBytes(sizeof (method_closure));
+    = (method_closure *)ffi_closure_alloc (sizeof (method_closure), &code);
 
   closure->arg_types[0] = &ffi_type_void;
 
@@ -1040,13 +1086,18 @@ _Jv_Linker::create_error_method (_Jv_Utf8Const *class_name)
                        1,
                        &ffi_type_void,
                       closure->arg_types) == FFI_OK
-      && ffi_prep_closure (&closure->closure,
-                           &closure->cif,
-                          _Jv_ThrowNoClassDefFoundErrorTrampoline,
-                          class_name) == FFI_OK)
-    return &closure->closure;
+      && ffi_prep_closure_loc (&closure->closure,
+                              &closure->cif,
+                              _Jv_ThrowNoClassDefFoundErrorTrampoline,
+                              class_name,
+                              code) == FFI_OK)
+    {
+      closure->list.registerClosure (klass, closure);
+      return code;
+    }
   else
     {
+      ffi_closure_free (closure);
       java::lang::StringBuffer *buffer = new java::lang::StringBuffer();
       buffer->append(JvNewStringLatin1("Error setting up FFI closure"
                                       " for static method of"
@@ -1057,7 +1108,7 @@ _Jv_Linker::create_error_method (_Jv_Utf8Const *class_name)
 }
 #else
 void *
-_Jv_Linker::create_error_method (_Jv_Utf8Const *)
+_Jv_Linker::create_error_method (_Jv_Utf8Const *, jclass)
 {
   // Codepath for platforms which do not support (or want) libffi.
   // You have to accept that it is impossible to provide the name
@@ -1088,8 +1139,6 @@ static bool debug_link = false;
 // at the corresponding position in the virtual method offset table
 // (klass->otable). 
 
-// The same otable and atable may be shared by many classes.
-
 // This must be called while holding the class lock.
 
 void
@@ -1240,13 +1289,15 @@ _Jv_Linker::link_symbol_table (jclass klass)
       // NullPointerException
       klass->atable->addresses[index] = NULL;
 
+      bool use_error_method = false;
+
       // If the target class is missing we prepare a function call
       // that throws a NoClassDefFoundError and store the address of
       // that newly prepared method in the atable. The user can run
       // code in classes where the missing class is part of the
       // execution environment as long as it is never referenced.
       if (target_class == NULL)
-        klass->atable->addresses[index] = create_error_method(sym.class_name);
+       use_error_method = true;
       // We're looking for a static field or a static method, and we
       // can tell which is needed by looking at the signature.
       else if (signature->first() == '(' && signature->len() >= 2)
@@ -1294,12 +1345,16 @@ _Jv_Linker::link_symbol_table (jclass klass)
                }
            }
          else
+           use_error_method = true;
+
+         if (use_error_method)
            klass->atable->addresses[index]
-              = create_error_method(sym.class_name);
+             = create_error_method(sym.class_name, klass);
 
          continue;
        }
 
+
       // Try fields only if the target class exists.
       if (target_class != NULL)
       {
@@ -1458,6 +1513,11 @@ _Jv_Linker::layout_vtable_methods (jclass klass)
                  sb->append(_Jv_GetMethodString(declarer, super_meth));
                  throw new VerifyError(sb->toString());
                }
+             else if (declarer->loader != klass->loader)
+               {
+                 // JVMS 5.4.2.
+                 check_loading_constraints (meth, klass, declarer);
+               }
            }
        }
 
@@ -1692,13 +1752,15 @@ _Jv_Linker::ensure_class_linked (jclass klass)
       // Resolve the remaining constant pool entries.
       for (int index = 1; index < pool->size; ++index)
        {
-         if (pool->tags[index] == JV_CONSTANT_String)
-           {
-             jstring str;
+         jbyte tags;
+         _Jv_word data;
 
-             str = _Jv_NewStringUtf8Const (pool->data[index].utf8);
-             pool->data[index].o = str;
-             pool->tags[index] |= JV_CONSTANT_ResolvedFlag;
+         tags = read_cpool_entry (&data, pool, index);
+         if (tags == JV_CONSTANT_String)
+           {
+             data.o = _Jv_NewStringUtf8Const (data.utf8);
+             tags |= JV_CONSTANT_ResolvedFlag;
+             write_cpool_entry (data, tags, pool, index);
            }
        }
 
@@ -1941,33 +2003,35 @@ _Jv_Linker::wait_for_state (jclass klass, int state)
   if (klass->state >= state)
     return;
 
-  JvSynchronize sync (klass);
-
-  // This is similar to the strategy for class initialization.  If we
-  // already hold the lock, just leave.
   java::lang::Thread *self = java::lang::Thread::currentThread();
-  while (klass->state <= state
-        && klass->thread 
-        && klass->thread != self)
-    klass->wait ();
 
-  java::lang::Thread *save = klass->thread;
-  klass->thread = self;
+  {
+    JvSynchronize sync (klass);
 
-  // Allocate memory for static fields and constants.
-  if (GC_base (klass) && klass->fields && ! GC_base (klass->fields))
-    {
-      jsize count = klass->field_count;
-      if (count)
-       {
-         _Jv_Field* fields 
-           = (_Jv_Field*) _Jv_AllocRawObj (count * sizeof (_Jv_Field));
-         memcpy ((void*)fields,
-                 (void*)klass->fields,
-                 count * sizeof (_Jv_Field));
-         klass->fields = fields;
-       }
-    }
+    // This is similar to the strategy for class initialization.  If we
+    // already hold the lock, just leave.
+    while (klass->state <= state
+          && klass->thread 
+          && klass->thread != self)
+      klass->wait ();
+
+    java::lang::Thread *save = klass->thread;
+    klass->thread = self;
+
+    // Allocate memory for static fields and constants.
+    if (GC_base (klass) && klass->fields && ! GC_base (klass->fields))
+      {
+       jsize count = klass->field_count;
+       if (count)
+         {
+           _Jv_Field* fields 
+             = (_Jv_Field*) _Jv_AllocRawObj (count * sizeof (_Jv_Field));
+           memcpy ((void*)fields,
+                   (void*)klass->fields,
+                   count * sizeof (_Jv_Field));
+           klass->fields = fields;
+         }
+      }
       
   // Print some debugging info if requested.  Interpreted classes are
   // handled in defineclass, so we only need to handle the two
@@ -1981,49 +2045,61 @@ _Jv_Linker::wait_for_state (jclass klass, int state)
       ++gcj::loadedClasses;
     }
 
-  try
-    {
-      if (state >= JV_STATE_LOADING && klass->state < JV_STATE_LOADING)
-       {
-         ensure_supers_installed (klass);
-         klass->set_state(JV_STATE_LOADING);
-       }
+    try
+      {
+       if (state >= JV_STATE_LOADING && klass->state < JV_STATE_LOADING)
+         {
+           ensure_supers_installed (klass);
+           klass->set_state(JV_STATE_LOADING);
+         }
 
-      if (state >= JV_STATE_LOADED && klass->state < JV_STATE_LOADED)
-       {
-         ensure_method_table_complete (klass);
-         klass->set_state(JV_STATE_LOADED);
-       }
+       if (state >= JV_STATE_LOADED && klass->state < JV_STATE_LOADED)
+         {
+           ensure_method_table_complete (klass);
+           klass->set_state(JV_STATE_LOADED);
+         }
 
-      if (state >= JV_STATE_PREPARED && klass->state < JV_STATE_PREPARED)
-       {
-         ensure_fields_laid_out (klass);
-         make_vtable (klass);
-         layout_interface_methods (klass);
-         prepare_constant_time_tables (klass);
-         klass->set_state(JV_STATE_PREPARED);
-       }
+       if (state >= JV_STATE_PREPARED && klass->state < JV_STATE_PREPARED)
+         {
+           ensure_fields_laid_out (klass);
+           make_vtable (klass);
+           layout_interface_methods (klass);
+           prepare_constant_time_tables (klass);
+           klass->set_state(JV_STATE_PREPARED);
+         }
 
-      if (state >= JV_STATE_LINKED && klass->state < JV_STATE_LINKED)
-       {
-         if (gcj::verifyClasses)
-           verify_class (klass);
+       if (state >= JV_STATE_LINKED && klass->state < JV_STATE_LINKED)
+         {
+           if (gcj::verifyClasses)
+             verify_class (klass);
 
-         ensure_class_linked (klass);
-         link_exception_table (klass);
-         link_symbol_table (klass);
-         klass->set_state(JV_STATE_LINKED);
-       }
-    }
-  catch (java::lang::Throwable *exc)
-    {
-      klass->thread = save;
-      klass->set_state(JV_STATE_ERROR);
-      throw exc;
-    }
+           ensure_class_linked (klass);
+           link_exception_table (klass);
+           link_symbol_table (klass);
+           klass->set_state(JV_STATE_LINKED);
+         }
+      }
+    catch (java::lang::Throwable *exc)
+      {
+       klass->thread = save;
+       klass->set_state(JV_STATE_ERROR);
+       throw exc;
+      }
 
-  klass->thread = save;
+    klass->thread = save;
 
-  if (klass->state == JV_STATE_ERROR)
-    throw new java::lang::LinkageError;
+    if (klass->state == JV_STATE_ERROR)
+      throw new java::lang::LinkageError;
+  }
+
+#ifdef INTERPRETER
+  if (__builtin_expect (klass->state == JV_STATE_LINKED, false)
+      && state >= JV_STATE_LINKED
+      && JVMTI_REQUESTED_EVENT (ClassPrepare))
+    {
+      JNIEnv *jni_env = _Jv_GetCurrentJNIEnv ();
+      _Jv_JVMTI_PostEvent (JVMTI_EVENT_CLASS_PREPARE, self, jni_env,
+                          klass);
+    }
+#endif
 }