On Tue, 8 Apr 2025 17:36:57 GMT, Ioi Lam <ik...@openjdk.org> wrote:

>> Timofei Pushkin has updated the pull request incrementally with one 
>> additional commit since the last revision:
>> 
>>   Remove unnecessary scoping
>
> src/java.base/share/classes/jdk/internal/misc/CDS.java line 438:
> 
>> 436:             // class loader. Thus it is safe to delegate their loading 
>> to system class loader
>> 437:             // (our parent) - this is what the default implementation 
>> of loadClass() will do.
>> 438:             return defineClass(name, bytes, 0, bytes.length);
> 
> I didn't realize that `URLClassLoader` will by default delegate to 
> `ClassLoader::getSystemClassLoader()`. How about rewording the comments like 
> this to clarify?
> 
> 
> // defineClass() will internally invoke loadClass() to load supertypes of 
> this unregistered class.
> // Any supertype S with the name SN must have already been loaded (enforced 
> by the order
> // of classes in the classlist). In addition:
> // - if S is an unregistered class, S must have already been have been 
> defined by the current class
> //   loader, and will be found by `this.findLoadedClass(SN)`
> // - if S is not an unregistered class, S must have already been defined by 
> the built-in boot,
> //   platform, or system class loaders, and can be found by 
> this.getParent().loadClass(SN, false)
> // See the implementation of ClassLoader::loadClass() for details.
> //
> // Therefore, we should resolve all supertypes to the expected ones as 
> specified by the
> // <code>super:</code> and <code>interfaces:</code> attributes in the 
> classlist. This
> // invariance is validated by the C++ function 
> ClassListParser::load_class_from_source()
> assert getParent() == getSystemClassLoader();
> return defineClass(name, bytes, 0, bytes.length);

I've actually noticed a problem here. I assumed that registered classes are 
always loaded by JDK's built-in class loaders (bootstrap, 
`jdk.internal.loader.ClassLoaders$PlatformClassLoader` or 
`jdk.internal.loader.ClassLoaders$AppClassLoader`). But in reality when the 
system class loader is user-provided (via `-Dsystem.class.loader=<class>`) 
classes loaded by it are still considered registered. This is a problem because 
such user-provided class loader may not delegate to real built-in class loaders.

For example:
- Let C=<N, L> be a class C named N defined by class loader L
- Let AppL be the built-in system class loader and SysL be the user-provided 
system class loader
- Let U be an unregistered class which extends a registered <S, AppL>
- Let SysL be able to load a registered <S, SysL> when requested (i.e. SysL 
doesn't delegate when loading a class named S)
- When `UnregisteredClassLoader` will be loading U it will delegate the loading 
of its super named S to SysL and thus <S, SysL> will become a super of U 
instead of <S, AppL> — this is an error (which will then be detected in 
`ClassListParser::load_class_from_source()` but still)

This won't be a problem if only classes loaded by the real built-in class 
loaders would be considered registered because such class loaders always 
delegate first (the 4th bullet above won't be possible), and thus it doesn't 
matter which of these loaders was used for delegation by the class loader which 
defined U.

This can't be fixed by just making 
`jdk.internal.loader.ClassLoaders$AppClassLoader` a parent of 
`UnregisteredClassLoader` and making `UnregisteredClassLoader.findClass(name)` 
return `getSystemClassLoader().loadClass(name)` because then the case when U 
extends <S, SysL> will not be handeled correctly (<S, AppL> will be used 
instead of <S, SysL>).

So it looks like I have to revert this delegation-based approach and use the 
old way.

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/24223#discussion_r2034846610

Reply via email to