RFR: 8263926: JavacFileManager.hasExplicitLocation fails with NPE while compiling [v2]

Jonathan Gibbons jjg at openjdk.java.net
Thu Jun 17 18:50:28 UTC 2021


On Thu, 17 Jun 2021 16:56:55 GMT, Guoxiong Li <gli at openjdk.org> wrote:

>> Hi all,
>> 
>> The compiler fails with NullPointerException while building the maven project provided by the user in [JDK-8263926](https://bugs.openjdk.java.net/browse/JDK-8263926).
>> 
>> Actually, the patch is simple. We only need to add a `null` check before using the `msym.sourceLocation`.
>> 
>> 
>> -            if (fileManager.contains(msym.sourceLocation, fo)) 
>> +            if (msym.sourceLocation != null && fileManager.contains(msym.sourceLocation, fo)) 
>> 
>> 
>> But we can't verify whether the problem is solved because the code provided by the user is complex so that we have no way to wtite a regression test.
>> 
>> Recently, I deeply researched the relationship of the source code provided by the user 
>> and construct a minimal test case to verify the patch. 
>> The minimal test was attached to the JBS just now.
>> The attached test reproduces the bug by using the shell script instead of maven.
>> And the test case `SourceLocationNotExist.java` at this patch is another way to reproduce the bug. 
>> The steps in `SourceLocationNotExist.java` is the same as the shell script mentioned above.
>> 
>> Given that the bug is hard to be reproduced, I give some comments here.
>> 
>> 1. We need to construct a jar file which has both `*.java` files and `*.class` files and has no named module.
>>     First, I compile the file `test/TestUnnamedModule.java` into `test/TestUnnamedModule.class`.
>>     Then  I use the `jar` tool to construct the jar file `test.jar`, which contains both `test/TestUnnamedModule.java` and `test/TestUnnamedModule.class`.
>>     Commands are like: 
>>     `javac test/TestUnnamedModule.java` and 
>>     `jar -c -f test.jar test/TestUnnamedModule.class test/TestUnnamedModule.java`
>> 
>> 2. The last modified time of the `*.java` files should be newer than that of the `*.class` files. 
>>     Because the compiler implicitly choose the last modified file if both source files and class files are present.
>>     We need the compiler the choose the `*.java` files to reproduce the bug.
>>     So before using the `jar` command, we need to update the modified time of the `*.java` files to let it newer than `*.class` files.
>>     Command is like: `touch test/TestUnnamedModule.java`.
>> 
>> 3. We need to use the class of the `test.jar` in a named module.
>>     I construct a module named `use`, which has a module file `module-info.java` and a source file `TestUse.java`.
>>     You can see the `TestUse.java` has a statement `import test.TestUnnamedModule;` to use the `test.jar`.
>> 
>> 4. The `module-info.java` of the module `use` can't have the directive `requires test`. Because the name `test` is the automatic module name of the jar file `test.jar`.
>> 
>> 5. When compiling the module `use` mentioned above, we must use the option `-sourcepath` and the `test.jar` is not  the argument of the `-sourcepath` and `--module-path`.
>>     Command is like: `javac -classpath test.jar -d use -sourcepath use use/module-info.java use/TestUse.java`
>> 
>> We can see that these five requirements are difficult to meet.
>> - Generally, our jar files shouldn't have the `*.java` files and only have the `*.class` files. But some libs that the user uses have these `*.java`files unexpectedly .
>> - And the class files are always compiled from the source files so that it is almost impossible that the last modified time of the `*.java` files is newer than `*.class` files.
>> - Generally, if we use the module system to construct our source code, we should compile the source code by using option `--module-path` instead of `-classpath`.
>> Command is like: javac **--module-path** test.jar -d use -sourcepath use use/module-info.java use/TestUse.java
>> instead of: javac **-classpath** test.jar -d use -sourcepath use use/module-info.java use/TestUse.java
>> However, the option `-classpath` is used by the maven at this situation (which no `requires` directive is in the `module-info.java`).
>> 
>> It is a combination of the java compiler(has no null check), the maven building tools(use `-classpath`), 
>> the extra libraries(have `*.java` files in the jar file) and the user's moduled project codes(have no corresponding module directive).
>> 
>> Anyway, we actually meet it now which is provided by the user.
>> Hope you are not confused by these descriptions.
>> 
>> Thanks for taking the time to review.
>> 
>> Best Regards,
>> -- Guoxiong
>
> Guoxiong Li has updated the pull request incrementally with one additional commit since the last revision:
> 
>   Adjust the comments

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java line 539:

> 537:         try {
> 538:             JavaFileObject fo = tree.sourcefile;
> 539:             if (msym.sourceLocation != null && fileManager.contains(msym.sourceLocation, fo)) {

What is the module for which the `sourceLocation` is `null` ... in other words, it is reasonable for this method to be called with a module with no source location, or is this check just hiding some other bug elsewhere?

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

PR: https://git.openjdk.java.net/jdk/pull/4523


More information about the compiler-dev mailing list