Hi, While digging around the class loading issues, I discovered that we didn't record a class with the "initiating loader" [1]. This is necessary to maintain type safety in the face of buggy or malicious class loaders (or even when the contents of the directories in the classpath change).
I've also attached a Mauve test case that checks for everything I could think of. These tests pass on IBM JDK 1.3, Sun JDK 1.4, Sun JDK 1.5 and my local IKVM version. Please comment. Regards, Jeroen [1] http://java.sun.com/j2se/1.5.0/docs/api/java/lang/ClassLoader.html#findL oadedClass(java.lang.String) 2005-07-27 Jeroen Frijters <[EMAIL PROTECTED]> * java/lang/Class.java (forName(String), forName(String,boolean,ClassLoader)): Added call to VMClassLoader.registerInitiatingLoader. * java/lang/ClassLoader.java (defineClass(String,byte[],int,int,ProtectionDomain): Changed to call VMClassLoader.registerInitiatingLoader. * vm/reference/java/lang/VMClassLoader.java (findLoadedClass): Added synchronization. (registerInitiatingLoader): New method.
Index: java/lang/Class.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/lang/Class.java,v
retrieving revision 1.37
diff -u -r1.37 Class.java
--- java/lang/Class.java 2 Jul 2005 20:32:38 -0000 1.37
+++ java/lang/Class.java 27 Jul 2005 11:13:13 -0000
@@ -156,11 +156,16 @@
*/
public static Class forName(String name) throws ClassNotFoundException
{
- Class result = VMClass.forName (name);
- if (result == null)
- result = Class.forName(name, true,
- VMStackWalker.getCallingClassLoader());
- return result;
+ ClassLoader cl = VMStackWalker.getCallingClassLoader();
+ Class result = VMClass.forName(name);
+ if (result != null)
+ {
+ if (cl != null)
+ VMClassLoader.registerInitiatingLoader(cl, result);
+ return result;
+ }
+ else
+ return forName(name, true, cl);
}
/**
@@ -216,13 +221,27 @@
}
throw new ClassNotFoundException(name);
}
- if (name.startsWith("["))
- return VMClass.loadArrayClass(name, classloader);
- Class c = classloader.loadClass(name);
- classloader.resolveClass(c);
- if (initialize)
- VMClass.initialize(c);
- return c;
+ else
+ {
+ // Take a shortcut, if possible. This is not just a performance
+ // tweak, but it also helps protect us from broken class loaders.
+ Class c = classloader.findLoadedClass(name);
+ if (c == null)
+ {
+ if (name.startsWith("["))
+ {
+ c = VMClass.loadArrayClass(name, classloader);
+ VMClassLoader.registerInitiatingLoader(classloader, c);
+ return c;
+ }
+ c = classloader.loadClass(name);
+ VMClassLoader.registerInitiatingLoader(classloader, c);
+ }
+ classloader.resolveClass(c);
+ if (initialize)
+ VMClass.initialize(c);
+ return c;
+ }
}
/**
Index: java/lang/ClassLoader.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/lang/ClassLoader.java,v
retrieving revision 1.54
diff -u -r1.54 ClassLoader.java
--- java/lang/ClassLoader.java 25 Jul 2005 14:28:42 -0000 1.54
+++ java/lang/ClassLoader.java 27 Jul 2005 11:13:14 -0000
@@ -480,7 +480,7 @@
Class retval = VMClassLoader.defineClass(this, name, data,
offset, len, domain);
if (! VMClassLoader.USE_VM_CACHE)
- loadedClasses.put(retval.getName(), retval);
+ VMClassLoader.registerInitiatingLoader(this, retval);
return retval;
}
Index: vm/reference/java/lang/VMClassLoader.java
===================================================================
RCS file:
/cvsroot/classpath/classpath/vm/reference/java/lang/VMClassLoader.java,v
retrieving revision 1.29
diff -u -r1.29 VMClassLoader.java
--- vm/reference/java/lang/VMClassLoader.java 26 Jul 2005 06:46:20 -0000
1.29
+++ vm/reference/java/lang/VMClassLoader.java 27 Jul 2005 11:13:19 -0000
@@ -292,9 +292,52 @@
/**
* If the VM wants to keep its own cache, this method can be replaced.
+ * If the name represents an array type, the ultimate component type
+ * is looked up and the array type is created.
*/
static Class findLoadedClass(ClassLoader cl, String name)
{
- return (Class) cl.loadedClasses.get(name);
+ synchronized (cl.loadedClasses)
+ {
+ return (Class) cl.loadedClasses.get(name);
+ }
+ }
+
+ /**
+ * This method is called by Class.forName() to notify the VM that a
+ * particular class loader should be recorded as the initiating loader for
+ * a class. Note that ClassLoader.findLoadedClass() should see classes that
+ * have been registered through this method.
+ * When the VM internally loads a class, it should call this method
+ * to record the class with the initiating class loader.
+ * Note that when an array type is registered, the ultimate component type
+ * is registered instead.
+ *
+ * @param cl Class loader instance (should not be null)
+ * @param c Class to be associated with this class loader
+ * @throws LinkageError if a different class with the same name was
+ * already loaded by this class loader.
+ */
+ static void registerInitiatingLoader(ClassLoader cl, Class c)
+ throws LinkageError
+ {
+ synchronized (cl.loadedClasses)
+ {
+ Object prev = cl.loadedClasses.get(c.getName());
+ if (prev != c)
+ {
+ if (prev != null)
+ throw new LinkageError("Duplicate class definition: "
+ + c.getName());
+ if (c.isArray())
+ {
+ Class componentType = c.getComponentType();
+ while (componentType.isArray())
+ componentType = componentType.getComponentType();
+ registerInitiatingLoader(cl, componentType);
+ }
+ cl.loadedClasses.put(c.getName(), c);
+ }
+ }
}
}
findLoadedClass.java
Description: findLoadedClass.java
_______________________________________________ Classpath-patches mailing list [email protected] http://lists.gnu.org/mailman/listinfo/classpath-patches
