This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: RFC: make linking BC-compiled classes more lazy - part one


This is OK, but with some changes that I'll note below.  These are
mostly typos and minor layout issues.

There is only one major thing we must see fixed, and that is exec
permission on the memory used by the FFI closure.

Once you have made these changes and you have copyright assignment you
don't need to post this patch again.

Andrew.


> Index: link.cc
> ===================================================================
> --- link.cc	(Revision 107719)
> +++ link.cc	(Arbeitskopie)
> @@ -34,6 +34,7 @@
>  #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 @@
>  // 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,9 +113,27 @@
>  	  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);

                 resolve_field (field, search->loader);

> +                }
> +              catch ( java::lang::Throwable *exc )

                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
>  	  // (and terminate the search) if it has the correct
> @@ -123,7 +142,10 @@
>  	  // 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 @@
>        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,19 @@
>  {
>    // 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());
> +  // 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

Two spaces after each full stop, please.  GNU coding convention.

> +  // resolution failed then we can only use the already given type name.
> +  jclass field_type = _Jv_FindClassFromSignatureNoException (
> +    field_type_name->chars(), klass->loader);

Thsi is slightly strange formatting.  Suggest

  jclass field_type 
    = _Jv_FindClassFromSignatureNoException (field_type_name->chars(), 
					     klass->loader);

>  
>    _Jv_Field *the_field = find_field_helper (owner, field_name,
> -					    field_type->name, found_class);
> +                                            (field_type ?
> +                                              field_type->name :
> +                                              field_type_name ),
> +                                            field_type, found_class);

Suggest

  _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 +222,12 @@
>        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 +255,7 @@
>  }
>  
>  _Jv_word
> -_Jv_Linker::resolve_pool_entry (jclass klass, int index)
> +_Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy)

Can you just do this with a default argument?  Like

_Jv_word
_Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy=false)

Then you won't need resolve_pool_entry (jclass, int)

>  {
>    using namespace java::lang::reflect;
>  
> @@ -238,13 +272,26 @@
>  
>  	jclass found;
>  	if (name->first() == '[')
> -	  found = _Jv_FindClassFromSignature (name->chars(),
> +	  found = _Jv_FindClassFromSignatureNoException (name->chars(),
>  					      klass->loader);

	  found = _Jv_FindClassFromSignatureNoException (name->chars(),
							 klass->loader);

>  	else
> -	  found = _Jv_FindClass (name, klass->loader);
> +	  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 witht the class'

spelling: "with", not "witht".

> +        // 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,8 +333,13 @@
>  	_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);
>  
> @@ -434,6 +486,13 @@
>    return pool->data[index];
>  }
>  
> +// A variant of resolve_pool_entry that does not permit lazy class loading.
> +_Jv_word
> +_Jv_Linker::resolve_pool_entry (jclass klass, int index)
> +{
> +  return resolve_pool_entry(klass, index, false);
> +}
> +

Don't need this.

>  // This function is used to lazily locate superclasses and
>  // superinterfaces.  This must be called with the class lock held.
>  void
> @@ -707,12 +766,26 @@
>    return buf->toString();
>  }
>  
> -void 
> +void
>  _Jv_ThrowNoSuchMethodError ()
>  {
>    throw new java::lang::NoSuchMethodError;
>  }
>  
> +// A function that whose invocation is prepared using libffi. It gets called

// A function whose...


> +// 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);
> +}
> +
> +
>  // This is put in empty vtable slots.
>  void
>  _Jv_ThrowAbstractMethodError ()
> @@ -990,21 +1063,55 @@
>         (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 to a function
> +      // that simply throws a NoClassDefFoundError and store the address in the
> +      // atable. The effect is that the user can run code of classes where the
> +      // missing class is part of the execution environment but gets never
> +      // referenced.

      // If the target class is missing we prepare a function call
      // that throws a NoClassDefFoundError and store the address of
      // that newly prepared function 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()));
> -      
> +        {
> +          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 * ));

We need to mprotect(PROT_READ | PROT_WRITE | PROT_EXEC) this memory or
alloc it from the heap.

> +          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());
> +            }
> +        }

Layout:

          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());
            }
        }

... but it may be better to break up the complex conditional.

>        // 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 
> @@ -1048,7 +1155,8 @@
>  	  continue;
>  	}
>  
> -      // Try fields.
> +      // Tries fields only if the target class exists.

Use of the imperative form in comments is OK: "try fields", not "tries
fields".

> +      if ( target_class != NULL )
>        {
>  	wait_for_state(target_class, JV_STATE_PREPARED);
>  	jclass found_class;
> @@ -1413,7 +1521,8 @@
>  	  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);
>  	    }
>  	}
>  
> @@ -1453,9 +1562,14 @@
>  	      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$
> -		  && (mod & java::lang::reflect::Modifier::STATIC) != 0)
> +
> +              // 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 )

Layout:

              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)

Also, it's probably better to factor this double nested conditional
into a statement and a test, like so:

	      bool foo 
		= (f->isResolved () 
		   ? (f->type == &java::lang::String::class$) 
		   : _Jv_equalUtf8Classnames((_Jv_Utf8Const *) f->type,
					     java::lang::String::class$.name));
              if (foo
		  && (mod & java::lang::reflect::Modifier::STATIC) != 0)


>  		{
>  		  jstring *strp = (jstring *) f->u.addr;
>  		  if (*strp)
> Index: verify.cc
> ===================================================================
> --- verify.cc	(Revision 107719)
> +++ verify.cc	(Arbeitskopie)
> @@ -14,6 +14,8 @@
>  
>  #include <config.h>
>  
> +#include <string.h>
> +
>  #include <jvm.h>
>  #include <gcj/cni.h>
>  #include <java-insns.h>
> @@ -324,7 +326,7 @@
>      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,22 @@
>        if (is_resolved)
>  	return;
>  
> +      // This is useful if you want to see which classes have to be resolved.
> +      // This is a good breakpoint if you want to find out why a class has to
> +      // be resolved.
> +      debug_print("resolving class: %s\n", data.name->chars());

I suggest we always do this if verbose:class is true.  Other VMs do.

> +
>        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] == ';')
> +      // Checking the first character being 'L' makes no sense, because
> +      // a package name may start with that letter.

This is a strange comment, given that after your patch is applied we
don't do this.

> +      // 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 +410,20 @@
>  	      // Avoid resolving if possible.
>  	      if (! self->is_resolved
>  		  && ! other_iter->is_resolved
> -		  && _Jv_equalUtf8Consts (self->data.name,
> +		  && _Jv_equalUtf8Classnames (self->data.name,
>  					  other_iter->data.name))

Layout:

		  && _Jv_equalUtf8Classnames (self->data.name,
					      other_iter->data.name))

>  		continue;
>  
>  	      if (! self->is_resolved)
>  		self->resolve(verifier);
> +
> +              // If the LHS of the expression is the java.lang.Object class
> +              // the assignment will succeed, no matter what the type of the
> +              // RHS is. Using this short-cut we can prevent that the RHS class
> +              // has to be resolved at verification time

              // 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 +873,70 @@
>        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()),
> +      // 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);

Layout:

          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);

              return type (verifier->make_utf8_const (arrayName, len + 3),
			   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
> Index: gcj/javaprims.h
> ===================================================================
> --- gcj/javaprims.h	(Revision 107719)
> +++ gcj/javaprims.h	(Arbeitskopie)
> @@ -566,6 +566,9 @@
>    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: java/lang/natClassLoader.cc
> ===================================================================
> --- java/lang/natClassLoader.cc	(Revision 107719)
> +++ java/lang/natClassLoader.cc	(Arbeitskopie)
> @@ -265,7 +265,31 @@
>    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)
>  {
>    // See if the class was already loaded by this loader.  This handles
> Index: java/lang/natClass.cc
> ===================================================================
> --- java/lang/natClass.cc	(Revision 107719)
> +++ java/lang/natClass.cc	(Arbeitskopie)
> @@ -668,8 +668,9 @@
>  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: java/lang/Class.h
> ===================================================================
> --- java/lang/Class.h	(Revision 107719)
> +++ java/lang/Class.h	(Arbeitskopie)
> @@ -54,7 +54,13 @@
>  
>    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;
> @@ -214,6 +220,8 @@
>  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);
> @@ -237,6 +245,8 @@
>  jboolean _Jv_IsInterpretedClass (jclass);
>  jboolean _Jv_IsBinaryCompatibilityABI (jclass);
>  
> +jboolean _Jv_IsPhantomClass (jclass);
> +
>  void _Jv_CopyClassesToSystemLoader (java::lang::ClassLoader *);
>  
>  #ifdef INTERPRETER
> @@ -443,6 +453,8 @@
>    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);
> @@ -473,6 +485,8 @@
>    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: include/jvm.h
> ===================================================================
> --- include/jvm.h	(Revision 107719)
> +++ include/jvm.h	(Arbeitskopie)
> @@ -243,7 +243,7 @@
>  {
>  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);
> @@ -276,6 +276,7 @@
>    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);
>    static void resolve_field (_Jv_Field *, java::lang::ClassLoader *);
>    static void verify_type_assertions (jclass);
>  };
> @@ -459,6 +460,8 @@
>  extern "C" void _Jv_RegisterResource (void *vptr);
>  extern void _Jv_UnregisterClass (_Jv_Utf8Const*, java::lang::ClassLoader*);
>  
> +extern jclass _Jv_FindClassNoException (_Jv_Utf8Const *name,
> +			     java::lang::ClassLoader *loader);
>  extern "C" jobject _Jv_UnwrapJNIweakReference (jobject);
>  
>  extern jclass _Jv_FindClass (_Jv_Utf8Const *name,
> @@ -466,6 +469,9 @@
>  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 +649,14 @@
>    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: ChangeLog
> ===================================================================
> --- ChangeLog	(Revision 107719)
> +++ ChangeLog	(Arbeitskopie)
> @@ -1,3 +1,24 @@
> +2005-11-23  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.
> +	(_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.
> +	* jvm.h:
> +	(_Jv_FindClassNoException): New method declaration.
> +	(_Jv_FindClassFromSignatureNoException): New method declaration.
> +	* prims.cc:
> +	(_Jv_FindClassFromSignatureNoException): New method.
> +
>  2005-11-30  Andrew Haley  <aph@redhat.com>
>  
>  	* classpath/lib/Makefile.am (resources): Use `cp -p'.
> Index: prims.cc
> ===================================================================
> --- prims.cc	(Revision 107719)
> +++ prims.cc	(Arbeitskopie)
> @@ -49,8 +49,10 @@
>  #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,8 +170,6 @@
>  }
>  #endif
>  
> -
> -

You deleted the ^L.  I guess this was a mistake.

>  jboolean
>  _Jv_equalUtf8Consts (const Utf8Const* a, const Utf8Const *b)
>  {
> @@ -236,6 +236,119 @@
>    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;
> +}
> +
> +// Finds out whether two _Jv_Utf8Const candidates contain the same classname.
// Find out whether two _Jv_Utf8Const candidates refer to 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 +547,9 @@
>  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 +628,11 @@
>  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,8 +887,28 @@
>    return result;
>  }
>  
> -

Another deleted ^L.

> +jclass
> +_Jv_FindClassFromSignatureNoException (char *sig, java::lang::ClassLoader *loader,
> +			    char **endp)

Layout:

_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 Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]