On Mon, 12 Aug 2024 17:23:15 GMT, Nizar Benalla <nbena...@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 build
> //        java.lang.BootstrapMethodError: bootstrap method initiali...

Changes requested by exe-b...@github.com (no known OpenJDK username).

> And this test cannot effectively test the cases where some object states will 
> have code paths that do not throw NPE.

**Ref:** [JDK‑8336430](https://bugs.openjdk.org/browse/JDK-8336430)

Since this now makes the NPE behaviour of [`Utf8Entry​::equalsString​(String)`] 
consistent, this PR can be marked as fixing [JDK‑8336430].

[`Utf8Entry​::equalsString​(String)`]: 
https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/classfile/constantpool/Utf8Entry.html#equalsString(java.lang.String)
[JDK‑8336430]: https://bugs.openjdk.org/browse/JDK-8336430

src/java.base/share/classes/java/lang/classfile/ClassHierarchyResolver.java 
line 88:

> 86:             //todo: uncomment later
> 87:             // this causes CorpusTest to fail
> 88:             // requireNonNull(superClass);

This is valid, as the super class descriptor of `java.lang.Object` is `null` 
(it’s the only one allowed and required to extend `null`).
Suggestion:

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)

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:

src/java.base/share/classes/java/lang/classfile/attribute/ModuleAttribute.java 
line 144:

> 142:         requireNonNull(moduleName);
> 143:         //todo should moduleVersion be Nullable?
> 144: //        requireNonNull(moduleVersion);

Module version is `null`able by Module spec.
Suggestion:

src/java.base/share/classes/java/lang/classfile/attribute/ModuleAttribute.java 
line 240:

> 238:             requireNonNull(module);
> 239:             requireNonNull(requiresFlags);
> 240:             requireNonNull(version);

Module version is `null`able by Module spec.
Suggestion:

src/java.base/share/classes/java/lang/classfile/attribute/ModuleRequireInfo.java
 line 93:

> 91:         requireNonNull(requires);
> 92:         //todo: uncomment later after CorpusTest is fixed
> 93: //        requireNonNull(requiresVersion);

Module version is `null`able by Module spec.
Suggestion:

src/java.base/share/classes/java/lang/classfile/attribute/ModuleRequireInfo.java
 line 104:

> 102:      */
> 103:     static ModuleRequireInfo of(ModuleEntry requires, 
> Collection<AccessFlag> requiresFlags, Utf8Entry requiresVersion) {
> 104:         requireNonNull(requiresVersion);

Module version is `null`able by Module spec.
Suggestion:

src/java.base/share/classes/java/lang/classfile/attribute/ModuleRequireInfo.java
 line 106:

> 104:         requireNonNull(requiresVersion);
> 105:         requireNonNull(requiresFlags);
> 106:         requireNonNull(requiresVersion);

Module version is `null`able by Module spec.
Suggestion:

src/java.base/share/classes/java/lang/classfile/attribute/ModuleRequireInfo.java
 line 118:

> 116:     static ModuleRequireInfo of(ModuleDesc requires, int requiresFlags, 
> String requiresVersion) {
> 117:         requireNonNull(requires);
> 118:         requireNonNull(requiresVersion);

Module version is `null`able by Module spec.
Suggestion:

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)

src/java.base/share/classes/jdk/internal/classfile/impl/ClassFileImpl.java line 
78:

> 76:     public ClassFileImpl withOptions(Option... options) {
> 77:         requireNonNull(options);
> 78:         for (var o : options){

Suggestion:

        for (var o : options) { // implicit null-check

src/java.base/share/classes/jdk/internal/classfile/impl/ClassRemapperImpl.java 
line 101:

> 99:         requireNonNull(clb);
> 100:         requireNonNull(cle);
> 101:         switch (cle) {

Suggestion:

        switch (cle) { // implicit null-check

src/java.base/share/classes/jdk/internal/classfile/impl/ClassRemapperImpl.java 
line 280:

> 278:     @Override
> 279:     public ClassDesc map(ClassDesc desc) {
> 280:         requireNonNull(desc);

Allowed below:
Suggestion:

src/java.base/share/classes/jdk/internal/classfile/impl/CodeLocalsShifterImpl.java
 line 54:

> 52:         Objects.requireNonNull(cob);
> 53:         Objects.requireNonNull(coe);
> 54:         switch (coe) {

Suggestion:

        switch (coe) { // implicit null-check

src/java.base/share/classes/jdk/internal/classfile/impl/CodeRelabelerImpl.java 
line 55:

> 53:         requireNonNull(cob);
> 54:         requireNonNull(coe);
> 55:         switch (coe) {

Suggestion:

        switch (coe) { // implicit null-check

src/java.base/share/classes/jdk/internal/classfile/impl/ModuleAttributeBuilderImpl.java
 line 83:

> 81:     @Override
> 82:     public ModuleAttributeBuilder moduleVersion(String version) {
> 83:         requireNonNull(version);

Module version is `null`able by Module spec.
Suggestion:

src/java.base/share/classes/jdk/internal/classfile/impl/ModuleAttributeBuilderImpl.java
 line 92:

> 90:         requireNonNull(module);
> 91:         // todo this crashes corpus test??
> 92: //        requireNonNull(version);

Module version is `null`able by Module spec.
Suggestion:

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

PR Review: https://git.openjdk.org/jdk/pull/20556#pullrequestreview-2278167656
PR Comment: https://git.openjdk.org/jdk/pull/20556#issuecomment-2288819265
PR Comment: https://git.openjdk.org/jdk/pull/20556#issuecomment-2325306146
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1742497978
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1742496132
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1742500730
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1742505667
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1742512822
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1742505172
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1742523836
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1742523525
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1742523581
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1739220025
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1742526544
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1742527119
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1742528052
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1742528767
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1742529154
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1742510632
PR Review Comment: https://git.openjdk.org/jdk/pull/20556#discussion_r1742510377

Reply via email to