On Tue, 3 Sep 2024 18:27:48 GMT, ExE Boss <d...@openjdk.org> wrote:

>> The test is inspired from [FFM API's 
>> TestNulls](https://github.com/openjdk/jdk/blob/master/test/jdk/java/foreign/TestNulls.java),
>>  I customized their Null checking framework it to work with ClassFile API.
>> 
>> The framework for for testing an API in bulk, so that all methods are 
>> reflectively called with some values replaced with nulls, so that all 
>> combinations are tried.
>> 
>> This test reveals some inconsistencies in the ClassFile API, whether it's a 
>> method with nullable arguments`[1]`, nulls are passed to`[2]` or its 
>> implementation handles nulls `[3]` `[4]`.
>> 
>> 
>> `[1]:` 
>> [ModuleRequireInfo#of](https://github.com/openjdk/jdk/blob/25e03b52094f46f89f2fe8f20e7e5622928add5f/src/java.base/share/classes/java/lang/classfile/attribute/ModuleRequireInfo.java#L89)
>> `[2]:` 
>> [Signature$ClassTypeSig#of](https://github.com/openjdk/jdk/blob/7ad61605f1669f51a97f4f263a7afaa9ab7706be/src/java.base/share/classes/java/lang/classfile/Signature.java#L150)
>> `[3]:`  
>> [CatchBuilderImpl#catching](https://github.com/openjdk/jdk/blob/7ad61605f1669f51a97f4f263a7afaa9ab7706be/src/java.base/share/classes/jdk/internal/classfile/impl/CatchBuilderImpl.java#L55)
>> `[4]:` 
>> [CodeBuilder#loadConstant](https://github.com/openjdk/jdk/blob/7ad61605f1669f51a97f4f263a7afaa9ab7706be/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java#L615)
>> 
>> 
>> 
>> `test/jdk/jdk/classfile/TestNullHostile.java#EXCLUDE_LIST` has the list of 
>> methods that are still not null hostile, they are ignored by the test, as 
>> making them null hostile either breaks some tests (listed below) or breaks 
>> the build with a `NullPointerException` or `BootstapMethodError`. I will 
>> paste the relevant methods from that list here.
>> 
>> Tests broken by these methods are :
>> 
>> 
>> jdk/classfile/VerifierSelfTest.java
>> jdk/classfile/TestRecordComponent.java
>> jdk/classfile/TestNullHostile.java   
>> jdk/classfile/OpcodesValidationTest.java
>> jdk/classfile/ClassPrinterTest.java
>> java/lang/invoke/MethodHandlesGeneralTest.java       
>> java/lang/invoke/MethodHandleProxies/WrapperHiddenClassTest.java
>> java/lang/invoke/MethodHandleProxies/WithSecurityManagerTest.java    
>> java/lang/invoke/MethodHandleProxies/BasicTest.java
>> 
>> 
>> Full list of methods
>> 
>> 
>> //the implementation of this method in CatchBuilderImpl handles nulls, is 
>> this fine?
>> "java.lang.classfile.CodeBuilder$CatchBuilder/catching(java.lang.constant.ClassDesc,java.util.function.Consumer)/0/0",
>> 
>> // making this method null-hostile causes a BootstapMethodError during the 
>> the...
>
> src/java.base/share/classes/java/lang/classfile/ClassHierarchyResolver.java 
> line 226:
> 
>> 224:     static ClassHierarchyResolver ofClassLoading(ClassLoader loader) {
>> 225:         //null check here breaks WithSecurityManagerTest
>> 226: //        requireNonNull(loader);
> 
> The `null` `ClassLoader` is valid and refers to either the system or 
> bootstrap class loader (depending on the API).
> 
> In the case of [`Class​::forName(…)`], it’s the bootstrap class loader (which 
> is represented by `null` (`Object.class.getClassLoader()` returns `null`!))
> Suggestion:
> 
> 
> 
> [`Class​::forName(…)`]: 
> https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/Class.html#forName(java.lang.String,boolean,java.lang.ClassLoader)

We intend to avoid using `null` to represent bootstrap class loader here, like 
in `MethodType`: 
https://github.com/openjdk/jdk/blob/bbb516163d400a9c7e923e423fe2a60091b59322/src/java.base/share/classes/java/lang/invoke/MethodType.java#L1197

> src/java.base/share/classes/java/lang/classfile/CodeBuilder.java line 622:
> 
>> 620:     default CodeBuilder loadConstant(ConstantDesc value) {
>> 621: //        adding null check here causes error
>> 622: //        requireNonNull(value);
> 
> This method is specified as allowing `null` below.
> Suggestion:

We are thinking about whether to allow nullable or enforce `Optional` across 
the ClassFile API. So this may be changed to accept an `Optional` in the future.

> src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java
>  line 385:
> 
>> 383:         @Override
>> 384:         public boolean equalsString(String s) {
>> 385:             Objects.requireNonNull(s);
> 
> I’d prefer if this method returned `false` on `null`, just like how 
> [`Object​::equals​(Object)`] and [`String​::equalsIgnoreCase​(String)`] 
> return `false` on `null`.
> 
> [`Object​::equals​(Object)`]: 
> https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/Object.html#equals(java.lang.Object)
> [`String​::equalsIgnoreCase​(String)`]: 
> https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/String.html#equalsIgnoreCase(java.lang.String)

We should have noted clearly that the intended use case is for a read utf8 to 
compare to an existing string, like a method name, so we can minimally inflate 
a utf8 to perform a comparison. This means that there is little purpose of 
using `null` here, just like for any other Class-File API endpoints.

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

PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1742619721
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1742622251
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1739266003

Reply via email to