2006-02-01 Robert Schuster <robertschuster@fsfe.org>
authorRobert Schuster <robertschuster@fsfe.org>
Wed, 1 Feb 2006 13:40:05 +0000 (13:40 +0000)
committerRobert Schuster <rschuster@gcc.gnu.org>
Wed, 1 Feb 2006 13:40:05 +0000 (13:40 +0000)
* link.cc:
(_Jv_Linker::find_field_helper): Added checks.
(_Jv_Linker::find_field): Use exception swallowing class resolution
and added early return.
(_Jv_ThrowNoClassDefFoundErrorTrampoline): New function.
(_Jv_Linker::link_symbol_table):  Use exception swallowing class
resolution, added ffi_closure installation routine, use
_Jv_ThrowNoClassDefFoundError for missing static method.
(_Jv_Linker::ensure_class_linked): Added string check which does
not trigger class resolution.
* java/lang/natClassLoader.cc:
(_Jv_FindClassNoException): New method.
* java/lang/Class.h:
(_Jv_FindClassNoException): New method declaration.
* include/jvm.h:
(_Jv_FindClassNoException): New method declaration.
(_Jv_FindClassFromSignatureNoException): New method declaration.
* prims.cc:
(_Jv_FindClassFromSignatureNoException): New method.
        * gcj/javaprims.h:
        (_Jv_equalsUtf8Classname): New method declaration.
        (_Jv_isPrimitiveOrDerived): Dito.
        * prims.cc:
(_Jv_equalsUtf8Classnames): New method.
        (_Jv_isPrimitiveOrDerived): New method.
        * verify.cc:
        (ref_intersection::equals): Use new classname comparison method.
        (type::compatible): Use new classname comparison method. Added
        check whether LHS' type is java.lang.Object .
        (type::resolve): Added new optional debug message and simplified
        if-expression.
        (type::to_array): Added codepath that generates an array type
        without resolving the element type.

From-SVN: r110474

libjava/ChangeLog
libjava/gcj/javaprims.h
libjava/include/jvm.h
libjava/java/lang/Class.h
libjava/java/lang/natClass.cc
libjava/java/lang/natClassLoader.cc
libjava/link.cc
libjava/prims.cc
libjava/verify.cc

index ec67fd7cb531267358ba51d6d952c69f59f4d5d3..ff1d6dd9a65b78dbd30f114f14e06d613174cd02 100644 (file)
@@ -1,3 +1,39 @@
+2006-02-01  Robert Schuster  <robertschuster@fsfe.org>
+
+       * link.cc:
+       (_Jv_Linker::find_field_helper): Added checks.
+       (_Jv_Linker::find_field): Use exception swallowing class resolution
+       and added early return.
+       (_Jv_ThrowNoClassDefFoundErrorTrampoline): New function.
+       (_Jv_Linker::link_symbol_table):  Use exception swallowing class
+       resolution, added ffi_closure installation routine, use
+       _Jv_ThrowNoClassDefFoundError for missing static method.
+       (_Jv_Linker::ensure_class_linked): Added string check which does
+       not trigger class resolution.
+       * java/lang/natClassLoader.cc:
+       (_Jv_FindClassNoException): New method.
+       * java/lang/Class.h:
+       (_Jv_FindClassNoException): New method declaration.
+       * include/jvm.h:
+       (_Jv_FindClassNoException): New method declaration.
+       (_Jv_FindClassFromSignatureNoException): New method declaration.
+       * prims.cc:
+       (_Jv_FindClassFromSignatureNoException): New method.
+        * gcj/javaprims.h:
+        (_Jv_equalsUtf8Classname): New method declaration.
+        (_Jv_isPrimitiveOrDerived): Dito.
+        * prims.cc:
+       (_Jv_equalsUtf8Classnames): New method.
+        (_Jv_isPrimitiveOrDerived): New method.
+        * verify.cc:
+        (ref_intersection::equals): Use new classname comparison method.
+        (type::compatible): Use new classname comparison method. Added
+        check whether LHS' type is java.lang.Object .
+        (type::resolve): Added new optional debug message and simplified
+        if-expression.
+        (type::to_array): Added codepath that generates an array type
+        without resolving the element type.
+
 2006-01-31  Mark Wielaard  <mark@klomp.org>
  
        * NEWS: Add 4.1 updates.
index 99fc7a3b345562d1747ae3f30471c0fde8e3f42c..c4f815f4d6b8c25420fc029814ecc3bbef578bf1 100644 (file)
@@ -566,6 +566,9 @@ class _Jv_Utf8Const
   friend jboolean _Jv_equalUtf8Consts (const _Jv_Utf8Const*, const _Jv_Utf8Const *);
   friend jboolean _Jv_equal (_Jv_Utf8Const*, jstring, jint);
   friend jboolean _Jv_equaln (_Jv_Utf8Const*, jstring, jint);
+  friend jboolean _Jv_equalUtf8Classnames (const _Jv_Utf8Const*,
+                                             const _Jv_Utf8Const*);
+  friend jboolean _Jv_isPrimitiveOrDerived (const _Jv_Utf8Const*);
   friend _Jv_Utf8Const *_Jv_makeUtf8Const (char*, int);
   friend _Jv_Utf8Const *_Jv_makeUtf8Const (jstring);
   friend jstring _Jv_NewStringUtf8Const (_Jv_Utf8Const*);
index ca0aea8000e1dfb5de643a0fd31d83a382b3b810..7110971f882004c42b430408b94fd6ff0fa1bd57 100644 (file)
@@ -239,7 +239,7 @@ class _Jv_Linker
 {
 private:
   static _Jv_Field *find_field_helper(jclass, _Jv_Utf8Const *, _Jv_Utf8Const *,
-                                     jclass *);
+                                     jclass, jclass *);
   static _Jv_Field *find_field(jclass, jclass, jclass *, _Jv_Utf8Const *,
                               _Jv_Utf8Const *);
   static void prepare_constant_time_tables(jclass);
@@ -271,7 +271,7 @@ public:
   static void print_class_loaded (jclass);
   static void resolve_class_ref (jclass, jclass *);
   static void wait_for_state(jclass, int);
-  static _Jv_word resolve_pool_entry (jclass, int);
+  static _Jv_word resolve_pool_entry (jclass, int, bool =false);
   static void resolve_field (_Jv_Field *, java::lang::ClassLoader *);
   static void verify_type_assertions (jclass);
 };
@@ -463,9 +463,18 @@ extern "C" jobject _Jv_UnwrapJNIweakReference (jobject);
 
 extern jclass _Jv_FindClass (_Jv_Utf8Const *name,
                             java::lang::ClassLoader *loader);
+
+extern jclass _Jv_FindClassNoException (_Jv_Utf8Const *name,
+                            java::lang::ClassLoader *loader);
+
 extern jclass _Jv_FindClassFromSignature (char *,
                                          java::lang::ClassLoader *loader,
                                          char ** = NULL);
+
+extern jclass _Jv_FindClassFromSignatureNoException (char *,
+                                         java::lang::ClassLoader *loader,
+                                         char ** = NULL);
+
 extern void _Jv_GetTypesFromSignature (jmethodID method,
                                       jclass declaringClass,
                                       JArray<jclass> **arg_types_out,
@@ -643,4 +652,14 @@ _Jv_IsBinaryCompatibilityABI (jclass c)
   return c->otable_syms || c->atable_syms || c->itable_syms;
 }
 
+// Returns whether the given class does not really exists (ie. we have no
+// bytecode) but still allows us to do some very conservative actions.
+// E.g. throwing a NoClassDefFoundError with the name of the missing
+// class.
+extern inline jboolean
+_Jv_IsPhantomClass (jclass c)
+{
+  return c->state == JV_STATE_PHANTOM;
+}
+
 #endif /* __JAVA_JVM_H__ */
index fe31fa2a39cecca2b8fef04205cac23be58047d4..2ddc8e16752edeaf9e47ee84241d6fdce3ce34e8 100644 (file)
@@ -69,7 +69,13 @@ enum
 
   JV_STATE_ERROR = 12,
 
-  JV_STATE_DONE = 14           // Must be last.
+  JV_STATE_PHANTOM = 13,       // Bytecode is missing. In many cases we can
+                                // work around that. If not, throw a
+                                // NoClassDefFoundError.
+
+  JV_STATE_DONE = 14,          // Must be last.
+
+
 };
 
 struct _Jv_Field;
@@ -240,6 +246,8 @@ void _Jv_RegisterClassHookDefault (jclass klass);
 void _Jv_RegisterInitiatingLoader (jclass,java::lang::ClassLoader*);
 void _Jv_UnregisterInitiatingLoader (jclass,java::lang::ClassLoader*);
 void _Jv_UnregisterClass (jclass);
+jclass _Jv_FindClassNoException (_Jv_Utf8Const *name,
+                     java::lang::ClassLoader *loader);
 jclass _Jv_FindClass (_Jv_Utf8Const *name,
                      java::lang::ClassLoader *loader);
 jclass _Jv_FindClassInCache (_Jv_Utf8Const *name);
@@ -263,6 +271,8 @@ jclass _Jv_GetArrayClass (jclass klass, java::lang::ClassLoader *loader);
 jboolean _Jv_IsInterpretedClass (jclass);
 jboolean _Jv_IsBinaryCompatibilityABI (jclass);
 
+jboolean _Jv_IsPhantomClass (jclass);
+
 void _Jv_CopyClassesToSystemLoader (gnu::gcj::runtime::SystemClassLoader *);
 
 #ifdef INTERPRETER
@@ -469,6 +479,8 @@ private:
   friend void ::_Jv_RegisterInitiatingLoader (jclass,java::lang::ClassLoader*);
   friend void ::_Jv_UnregisterInitiatingLoader (jclass,java::lang::ClassLoader*);
   friend void ::_Jv_UnregisterClass (jclass);
+  friend jclass (::_Jv_FindClassNoException) (_Jv_Utf8Const *name,
+                                  java::lang::ClassLoader *loader);
   friend jclass (::_Jv_FindClass) (_Jv_Utf8Const *name,
                                   java::lang::ClassLoader *loader);
   friend jclass (::_Jv_FindClassInCache) (_Jv_Utf8Const *name);
@@ -499,6 +511,8 @@ private:
   friend jboolean (::_Jv_IsInterpretedClass) (jclass);
   friend jboolean (::_Jv_IsBinaryCompatibilityABI) (jclass);
 
+  friend jboolean (::_Jv_IsPhantomClass) (jclass);
+
 #ifdef INTERPRETER
   friend void ::_Jv_InitField (jobject, jclass, int);
 
index 951bab974cc9c5eb1ff6c86d950697befb7f4b4c..04a5bc46310b5b821ad5fffc9228e31d7a73cf11 100644 (file)
@@ -668,8 +668,9 @@ java::lang::Class::finalize (void)
 void
 java::lang::Class::initializeClass (void)
 {
-  // Short-circuit to avoid needless locking.
-  if (state == JV_STATE_DONE)
+  // Short-circuit to avoid needless locking (expression includes
+  // JV_STATE_PHANTOM and JV_STATE_DONE).
+  if (state >= JV_STATE_PHANTOM)
     return;
 
   // Step 1.  We introduce a new scope so we can synchronize more
index 797005b54c7c495efc4e47dd451422d3d7880989..5f4d957f61cb11f9d87a98ec006f3d5ee64a7d83 100644 (file)
@@ -266,6 +266,30 @@ _Jv_CopyClassesToSystemLoader (gnu::gcj::runtime::SystemClassLoader *loader)
   system_class_list = SYSTEM_LOADER_INITIALIZED;
 }
 
+// An internal variant of _Jv_FindClass which simply swallows a
+// NoClassDefFoundError or a ClassNotFoundException. This gives the
+// caller a chance to evaluate the situation and behave accordingly.
+jclass
+_Jv_FindClassNoException (_Jv_Utf8Const *name, java::lang::ClassLoader *loader)
+{
+  jclass klass;
+
+  try
+    {
+      klass = _Jv_FindClass(name, loader);
+    }
+  catch ( java::lang::NoClassDefFoundError *ncdfe )
+    {
+      return NULL;
+    }
+  catch ( java::lang::ClassNotFoundException *cnfe )
+    {
+      return NULL;
+    }
+
+  return klass;
+}
+
 jclass
 _Jv_FindClass (_Jv_Utf8Const *name, java::lang::ClassLoader *loader)
 {
index 7070d729b4a01588caffadfee6eeed18b242e681..1118d9b8bab9bf28a5463e79abfa6b3b98b8b86c 100644 (file)
@@ -34,6 +34,7 @@ details.  */
 #include <java/lang/NoSuchMethodError.h>
 #include <java/lang/ClassFormatError.h>
 #include <java/lang/IllegalAccessError.h>
+#include <java/lang/InternalError.h>
 #include <java/lang/AbstractMethodError.h>
 #include <java/lang/NoClassDefFoundError.h>
 #include <java/lang/IncompatibleClassChangeError.h>
@@ -100,7 +101,7 @@ _Jv_Linker::resolve_field (_Jv_Field *field, java::lang::ClassLoader *loader)
 // superclasses and interfaces.
 _Jv_Field *
 _Jv_Linker::find_field_helper (jclass search, _Jv_Utf8Const *name,
-                              _Jv_Utf8Const *type_name,
+                              _Jv_Utf8Const *type_name, jclass type,
                               jclass *declarer)
 {
   while (search)
@@ -112,8 +113,26 @@ _Jv_Linker::find_field_helper (jclass search, _Jv_Utf8Const *name,
          if (! _Jv_equalUtf8Consts (field->name, name))
            continue;
 
-         if (! field->isResolved ())
-           resolve_field (field, search->loader);
+          // Checks for the odd situation where we were able to retrieve the
+          // field's class from signature but the resolution of the field itself
+          // failed which means a different class was resolved.
+          if (type != NULL)
+            {
+              try
+                {
+                  resolve_field (field, search->loader);
+                }
+              catch (java::lang::Throwable *exc)
+                {
+                  java::lang::LinkageError *le = new java::lang::LinkageError
+                   (JvNewStringLatin1 
+                      ("field type mismatch with different loaders"));
+
+                  le->initCause(exc);
+
+                  throw le;
+                }
+            }
 
          // Note that we compare type names and not types.  This is
          // bizarre, but we do it because we want to find a field
@@ -123,7 +142,10 @@ _Jv_Linker::find_field_helper (jclass search, _Jv_Utf8Const *name,
          // 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))
+         if ( (field->isResolved () ? 
+                _Jv_equalUtf8Classnames (type_name, field->type->name) :
+                _Jv_equalUtf8Classnames (
+                  type_name, (_Jv_Utf8Const *) field->type)) )
            {
              *declarer = search;
              return field;
@@ -134,7 +156,7 @@ _Jv_Linker::find_field_helper (jclass search, _Jv_Utf8Const *name,
       for (int i = 0; i < search->interface_count; ++i)
        {
          _Jv_Field *result = find_field_helper (search->interfaces[i], name,
-                                                type_name, declarer);
+                                                type_name, type, declarer);
          if (result)
            return result;
        }
@@ -175,13 +197,21 @@ _Jv_Linker::find_field (jclass klass, jclass owner,
 {
   // 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);
+  // Note: This call will resolve the primitive type names ("Z", "B", ...) to
+  // their Java counterparts ("boolean", "byte", ...) if accessed via
+  // field_type->name later.  Using these variants of the type name is in turn
+  // important for the find_field_helper function.  However if the class
+  // resolution failed then we can only use the already given type name.
+  jclass field_type 
+    = _Jv_FindClassFromSignatureNoException (field_type_name->chars(),
+                                             klass->loader);
+
+  _Jv_Field *the_field
+    = find_field_helper (owner, field_name,
+                         (field_type
+                           ? field_type->name :
+                             field_type_name ),
+                           field_type, found_class);
 
   if (the_field == 0)
     {
@@ -194,6 +224,12 @@ _Jv_Linker::find_field (jclass klass, jclass owner,
       throw new java::lang::NoSuchFieldError (sb->toString());
     }
 
+  // Accept it when the field's class could not be resolved.
+  if (field_type == NULL)
+    // Silently ignore that we were not able to retrieve the type to make it
+    // possible to run code which does not access this field.
+    return the_field;
+
   if (_Jv_CheckAccess (klass, *found_class, the_field->flags))
     {
       // Note that the field returned by find_field_helper is always
@@ -221,7 +257,7 @@ _Jv_Linker::find_field (jclass klass, jclass owner,
 }
 
 _Jv_word
-_Jv_Linker::resolve_pool_entry (jclass klass, int index)
+_Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy)
 {
   using namespace java::lang::reflect;
 
@@ -238,13 +274,26 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index)
 
        jclass found;
        if (name->first() == '[')
-         found = _Jv_FindClassFromSignature (name->chars(),
-                                             klass->loader);
-       else
-         found = _Jv_FindClass (name, klass->loader);
-
+         found = _Jv_FindClassFromSignatureNoException (name->chars(),
+                                                        klass->loader);
+        else
+         found = _Jv_FindClassNoException (name, klass->loader);
+
+        // If the class could not be loaded a phantom class is created. Any
+        // function that deals with such a class but cannot do something useful
+        // with it should just throw a NoClassDefFoundError with the class'
+        // name.
        if (! found)
-         throw new java::lang::NoClassDefFoundError (name->toString());
+          if (lazy)
+            {
+              found = _Jv_NewClass(name, NULL, NULL);
+              found->state = JV_STATE_PHANTOM;
+              pool->tags[index] |= JV_CONSTANT_ResolvedFlag;
+              pool->data[index].clazz = found;
+              break;
+            }
+          else
+           throw new java::lang::NoClassDefFoundError (name->toString());
 
        // Check accessibility, but first strip array types as
        // _Jv_ClassNameSamePackage can't handle arrays.
@@ -286,7 +335,12 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index)
        _Jv_loadIndexes (&pool->data[index],
                         class_index,
                         name_and_type_index);
-       jclass owner = (resolve_pool_entry (klass, class_index)).clazz;
+       jclass owner = (resolve_pool_entry (klass, class_index, true)).clazz;
+
+        // If a phantom class was resolved our field reference is
+        // unusable because of the missing class.
+        if (owner->state == JV_STATE_PHANTOM)
+          throw new java::lang::NoClassDefFoundError(owner->getName());
 
        if (owner != klass)
          _Jv_InitClass (owner);
@@ -707,12 +761,31 @@ _Jv_GetMethodString (jclass klass, _Jv_Method *meth,
   return buf->toString();
 }
 
-void 
+void
 _Jv_ThrowNoSuchMethodError ()
 {
   throw new java::lang::NoSuchMethodError;
 }
 
+// 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.
+// The prepared function call is stored in a class' atable.
+void
+_Jv_ThrowNoClassDefFoundErrorTrampoline(ffi_cif *,
+                                        void *,
+                                        void **,
+                                        void *data)
+{
+  throw new java::lang::NoClassDefFoundError((jstring) data);
+}
+
+void
+_Jv_ThrowNoClassDefFoundError()
+{
+  throw new java::lang::NoClassDefFoundError();
+}
+
 // Throw a NoSuchFieldError.  Called by compiler-generated code when
 // an otable entry is zero.  OTABLE_INDEX is the index in the caller's
 // otable that refers to the missing field.  This index may be used to
@@ -723,7 +796,6 @@ _Jv_ThrowNoSuchFieldError (int /* otable_index */)
   throw new java::lang::NoSuchFieldError;
 }
 
-
 // This is put in empty vtable slots.
 void
 _Jv_ThrowAbstractMethodError ()
@@ -1030,21 +1102,65 @@ _Jv_Linker::link_symbol_table (jclass klass)
        (sym = klass->atable_syms[index]).class_name != NULL;
        ++index)
     {
-      jclass target_class = _Jv_FindClass (sym.class_name, klass->loader);
+      jclass target_class =
+        _Jv_FindClassNoException (sym.class_name, klass->loader);
+
       _Jv_Method *meth = NULL;            
       _Jv_Utf8Const *signature = sym.signature;
 
       // ??? Setting this pointer to null will at least get us a
       // NullPointerException
       klass->atable->addresses[index] = NULL;
-      
+
+      // If the target class is missing we prepare a function call
+      // that throws a NoClassDefFoundError and store the address of
+      // that newly prepare 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)
-       throw new java::lang::NoClassDefFoundError 
-         (_Jv_NewStringUTF (sym.class_name->chars()));
-      
+        {
+          // TODO: The following structs/objects are heap allocated are
+          // unreachable by the garbage collector:
+          // - cif, arg_types
+          // - the Java string inside the if-statement
+
+          ffi_closure *closure =
+            (ffi_closure *) _Jv_Malloc( sizeof( ffi_closure ));
+          ffi_cif *cif = (ffi_cif *) _Jv_Malloc( sizeof( ffi_cif ));
+
+          // Pretends that we want to call a void (*) (void) function via
+          // ffi_call.
+          ffi_type **arg_types = (ffi_type **) _Jv_Malloc( sizeof( ffi_type * ));
+          arg_types[0] = &ffi_type_void;
+
+          // Initializes the cif and the closure. If that worked the closure is
+          // stored as a function pointer in the atable.
+          if ( ffi_prep_cif(cif, FFI_DEFAULT_ABI, 1,
+                            &ffi_type_void, arg_types) == FFI_OK
+               && (ffi_prep_closure 
+                   (closure, cif,
+                   _Jv_ThrowNoClassDefFoundErrorTrampoline,
+                   (void *) _Jv_NewStringUtf8Const(sym.class_name))
+                   == FFI_OK))
+            {
+              klass->atable->addresses[index] = (void *) closure;
+            }
+          else
+            {
+              // If you land here it is possible that your architecture does
+              // not support the Closure API yet. Let's port it!
+              java::lang::StringBuffer *buffer = new java::lang::StringBuffer();
+              buffer->append 
+                (JvNewStringLatin1("Error setting up FFI closure"
+                                   " for static method of missing class: "));
+              buffer->append (_Jv_NewStringUtf8Const(sym.class_name));
+
+              throw new java::lang::InternalError(buffer->toString());
+            }
+        }
       // We're looking for a static field or a static method, and we
       // can tell which is needed by looking at the signature.
-      if (signature->first() == '(' && signature->len() >= 2)
+      else if (signature->first() == '(' && signature->len() >= 2)
        {
          // If the target class does not have a vtable_method_count yet, 
          // then we can't tell the offsets for its methods, so we must lay 
@@ -1082,13 +1198,16 @@ _Jv_Linker::link_symbol_table (jclass klass)
                }
            }
          else
+            // TODO: Use _Jv_ThrowNoClassDefFoundErrorTrampoline to be able
+            // to print the class name.
            klass->atable->addresses[index]
-             = (void *)_Jv_ThrowNoSuchMethodError;
+               = (void *) _Jv_ThrowNoClassDefFoundError;
 
          continue;
        }
 
-      // Try fields.
+      // Try fields only if the target class exists.
+      if ( target_class != NULL )
       {
        wait_for_state(target_class, JV_STATE_PREPARED);
        jclass found_class;
@@ -1453,7 +1572,8 @@ _Jv_Linker::ensure_class_linked (jclass klass)
          for (int index = 1; index < pool->size; ++index)
            {
              if (pool->tags[index] == JV_CONSTANT_Class)
-               resolve_pool_entry (klass, index);
+                // Lazily resolve the entries.
+               resolve_pool_entry (klass, index, true);
            }
        }
 
@@ -1493,8 +1613,13 @@ _Jv_Linker::ensure_class_linked (jclass klass)
              int mod = f->getModifiers ();
              // If we have a static String field with a non-null initial
              // value, we know it points to a Utf8Const.
-             resolve_field(f, klass->loader);
-             if (f->getClass () == &java::lang::String::class$
+
+              // Finds out whether we have to initialize a String without the
+              // need to resolve the field.
+              if ((f->isResolved()
+                   ? (f->type == &java::lang::String::class$)
+                   : _Jv_equalUtf8Classnames((_Jv_Utf8Const *) f->type,
+                                             java::lang::String::class$.name))
                  && (mod & java::lang::reflect::Modifier::STATIC) != 0)
                {
                  jstring *strp = (jstring *) f->u.addr;
index a968a9b3013f7be844833330518dad63cc96daaa..0352669315dd0b9f5501e14245746fedcbf7ebf1 100644 (file)
@@ -49,8 +49,10 @@ details.  */
 #include <java/lang/ArrayIndexOutOfBoundsException.h>
 #include <java/lang/ArithmeticException.h>
 #include <java/lang/ClassFormatError.h>
+#include <java/lang/ClassNotFoundException.h>
 #include <java/lang/InternalError.h>
 #include <java/lang/NegativeArraySizeException.h>
+#include <java/lang/NoClassDefFoundError.h>
 #include <java/lang/NullPointerException.h>
 #include <java/lang/OutOfMemoryError.h>
 #include <java/lang/System.h>
@@ -168,7 +170,6 @@ SIGNAL_HANDLER (catch_fpe)
 }
 #endif
 
-\f
 
 jboolean
 _Jv_equalUtf8Consts (const Utf8Const* a, const Utf8Const *b)
@@ -236,6 +237,120 @@ _Jv_equaln (Utf8Const *a, jstring str, jint n)
   return true;
 }
 
+// Determines whether the given Utf8Const object contains
+// a type which is primitive or some derived form of it, eg.
+// an array or multi-dimensional array variant.
+jboolean
+_Jv_isPrimitiveOrDerived(const Utf8Const *a)
+{
+  unsigned char *aptr = (unsigned char *) a->data;
+  unsigned char *alimit = aptr + a->length;
+  int ac = UTF8_GET(aptr, alimit);
+
+  // Skips any leading array marks.
+  while (ac == '[')
+    ac = UTF8_GET(aptr, alimit);
+
+  // There should not be another character. This implies that
+  // the type name is only one character long.
+  if (UTF8_GET(aptr, alimit) == -1)
+    switch ( ac )
+      {
+        case 'Z':
+        case 'B':
+        case 'C':
+        case 'S':
+        case 'I':
+        case 'J':
+        case 'F':
+        case 'D':
+          return true;
+        default:
+          break;
+       }
+
+   return false;
+}
+
+// Find out whether two _Jv_Utf8Const candidates contain the same
+// classname.
+// The method is written to handle the different formats of classnames.
+// Eg. "Ljava/lang/Class;", "Ljava.lang.Class;", "java/lang/Class" and
+// "java.lang.Class" will be seen as equal.
+// Warning: This function is not smart enough to declare "Z" and "boolean"
+// and similar cases as equal (and is not meant to be used this way)!
+jboolean
+_Jv_equalUtf8Classnames (const Utf8Const *a, const Utf8Const *b)
+{
+  // If the class name's length differs by two characters
+  // it is possible that we have candidates which are given
+  // in the two different formats ("Lp1/p2/cn;" vs. "p1/p2/cn")
+  switch (a->length - b->length)
+    {
+      case -2:
+      case 0:
+      case 2:
+        break;
+      default:
+        return false;
+    }
+
+  unsigned char *aptr = (unsigned char *) a->data;
+  unsigned char *alimit = aptr + a->length;
+  unsigned char *bptr = (unsigned char *) b->data;
+  unsigned char *blimit = bptr + b->length;
+
+  if (alimit[-1] == ';')
+    alimit--;
+
+  if (blimit[-1] == ';')
+    blimit--;
+
+  int ac = UTF8_GET(aptr, alimit);
+  int bc = UTF8_GET(bptr, blimit);
+
+  // Checks whether both strings have the same amount of leading [ characters.
+  while (ac == '[')
+    {
+      if (bc == '[')
+        {
+          ac = UTF8_GET(aptr, alimit);
+          bc = UTF8_GET(bptr, blimit);
+          continue;
+        }
+
+      return false;
+    }
+
+  // Skips leading L character.
+  if (ac == 'L')
+    ac = UTF8_GET(aptr, alimit);
+        
+  if (bc == 'L')
+    bc = UTF8_GET(bptr, blimit);
+
+  // Compares the remaining characters.
+  while (ac != -1 && bc != -1)
+    {
+      // Replaces package separating dots with slashes.
+      if (ac == '.')
+        ac = '/';
+
+      if (bc == '.')
+        bc = '/';
+      
+      // Now classnames differ if there is at least one non-matching
+      // character.
+      if (ac != bc)
+        return false;
+
+      ac = UTF8_GET(aptr, alimit);
+      bc = UTF8_GET(bptr, blimit);
+    }
+
+  return (ac == bc);
+}
+
 /* Count the number of Unicode chars encoded in a given Ut8 string. */
 int
 _Jv_strLengthUtf8(char* str, int len)
@@ -434,6 +549,9 @@ _Jv_AllocObjectNoInitNoFinalizer (jclass klass)
 jobject
 _Jv_AllocObjectNoFinalizer (jclass klass)
 {
+  if (_Jv_IsPhantomClass(klass) )
+    throw new java::lang::NoClassDefFoundError(klass->getName());
+
   _Jv_InitClass (klass);
   jint size = klass->size ();
   jobject obj = (jobject) _Jv_AllocObj (size, klass);
@@ -512,6 +630,11 @@ _Jv_AllocPtrFreeObject (jclass klass)
 jobjectArray
 _Jv_NewObjectArray (jsize count, jclass elementClass, jobject init)
 {
+  // Creating an array of an unresolved type is impossible. So we throw
+  // the NoClassDefFoundError.
+  if ( _Jv_IsPhantomClass(elementClass) )
+    throw new java::lang::NoClassDefFoundError(elementClass->getName());
+
   if (__builtin_expect (count < 0, false))
     throw new java::lang::NegativeArraySizeException;
 
@@ -766,7 +889,28 @@ _Jv_FindClassFromSignature (char *sig, java::lang::ClassLoader *loader,
   return result;
 }
 
-\f
+
+jclass
+_Jv_FindClassFromSignatureNoException (char *sig, java::lang::ClassLoader *loader,
+                                       char **endp)
+{
+  jclass klass;
+
+  try
+    {
+      klass = _Jv_FindClassFromSignature(sig, loader, endp);
+    }
+  catch (java::lang::NoClassDefFoundError *ncdfe)
+    {
+      return NULL;
+    }
+  catch (java::lang::ClassNotFoundException *cnfe)
+    {
+      return NULL;
+    }
+
+  return klass;
+}
 
 JArray<jstring> *
 JvConvertArgv (int argc, const char **argv)
index 8b9cfcc759fae5dc8dc828087529535f9e068e30..4df6ead95c5215e40a4c22e6f29e9a84cef8d5a2 100644 (file)
@@ -14,6 +14,8 @@ details.  */
 
 #include <config.h>
 
+#include <string.h>
+
 #include <jvm.h>
 #include <gcj/cni.h>
 #include <java-insns.h>
@@ -324,7 +326,7 @@ private:
     bool equals (ref_intersection *other, _Jv_BytecodeVerifier *verifier)
     {
       if (! is_resolved && ! other->is_resolved
-         && _Jv_equalUtf8Consts (data.name, other->data.name))
+         && _Jv_equalUtf8Classnames (data.name, other->data.name))
        return true;
       if (! is_resolved)
        resolve (verifier);
@@ -364,11 +366,18 @@ private:
       if (is_resolved)
        return;
 
+      // This is useful if you want to see which classes have to be resolved
+      // while doing the class verification.
+      debug_print("resolving class: %s\n", data.name->chars());
+
       using namespace java::lang;
       java::lang::ClassLoader *loader
        = verifier->current_class->getClassLoaderInternal();
-      // We might see either kind of name.  Sigh.
-      if (data.name->first() == 'L' && data.name->limit()[-1] == ';')
+
+      // Due to special handling in to_array() array classes will always
+      // be of the "L ... ;" kind. The separator char ('.' or '/' may vary
+      // however.
+      if (data.name->limit()[-1] == ';')
        {
          data.klass = _Jv_FindClassFromSignature (data.name->chars(), loader);
          if (data.klass == NULL)
@@ -397,12 +406,21 @@ private:
              // Avoid resolving if possible.
              if (! self->is_resolved
                  && ! other_iter->is_resolved
-                 && _Jv_equalUtf8Consts (self->data.name,
-                                         other_iter->data.name))
+                 && _Jv_equalUtf8Classnames (self->data.name,
+                                             other_iter->data.name))
                continue;
 
              if (! self->is_resolved)
                self->resolve(verifier);
+
+              // If the LHS of the expression is of type
+              // java.lang.Object, assignment will succeed, no matter
+              // what the type of the RHS is. Using this short-cut we
+              // don't need to resolve the class of the RHS at
+              // verification time.
+              if (self->data.klass == &java::lang::Object::class$)
+                continue;
+
              if (! other_iter->is_resolved)
                other_iter->resolve(verifier);
 
@@ -852,9 +870,70 @@ private:
       if (key != reference_type)
        verifier->verify_fail ("internal error in type::to_array()");
 
-      jclass k = klass->getclass (verifier);
-      return type (_Jv_GetArrayClass (k, k->getClassLoaderInternal()),
-                  verifier);
+      // In case the class is already resolved we can simply ask the runtime
+      // to give us the array version.
+      // If it is not resolved we prepend "[" to the classname to make the
+      // array usage verification more lazy. In other words: makes new Foo[300]
+      // pass the verifier if Foo.class is missing.
+      if (klass->is_resolved)
+        {
+          jclass k = klass->getclass (verifier);
+
+          return type (_Jv_GetArrayClass (k, k->getClassLoaderInternal()),
+                      verifier);
+        }
+      else
+        {
+          int len = klass->data.name->len();
+
+          // If the classname is given in the Lp1/p2/cn; format we only need
+          // to add a leading '['. The same procedure has to be done for
+          // primitive arrays (ie. provided "[I", the result should be "[[I".
+          // If the classname is given as p1.p2.cn we have to embed it into
+          // "[L" and ';'.
+          if (klass->data.name->limit()[-1] == ';' ||
+               _Jv_isPrimitiveOrDerived(klass->data.name))
+            {
+              // Reserves space for leading '[' and trailing '\0' .
+              char arrayName[len + 2];
+
+              arrayName[0] = '[';
+              strcpy(&arrayName[1], klass->data.name->chars());
+
+#ifdef VERIFY_DEBUG
+              // This is only needed when we want to print the string to the
+              // screen while debugging.
+              arrayName[len + 1] = '\0';
+
+              debug_print("len: %d - old: '%s' - new: '%s'\n", len, klass->data.name->chars(), arrayName);
+#endif
+
+              return type (verifier->make_utf8_const( arrayName, len + 1 ),
+                           verifier);
+            }
+           else
+            {
+              // Reserves space for leading "[L" and trailing ';' and '\0' .
+              char arrayName[len + 4];
+
+              arrayName[0] = '[';
+              arrayName[1] = 'L';
+              strcpy(&arrayName[2], klass->data.name->chars());
+              arrayName[len + 2] = ';';
+
+#ifdef VERIFY_DEBUG
+              // This is only needed when we want to print the string to the
+              // screen while debugging.
+              arrayName[len + 3] = '\0';
+
+              debug_print("len: %d - old: '%s' - new: '%s'\n", len, klass->data.name->chars(), arrayName);
+#endif
+
+              return type (verifier->make_utf8_const( arrayName, len + 3 ),
+                           verifier);
+            }
+        }
+
     }
 
     bool isreference () const