RFR: 8296656: java.lang.NoClassDefFoundError exception on running fully legitimate code [v7]
David Alayachew
duke at openjdk.org
Mon Apr 24 16:18:05 UTC 2023
On Sat, 25 Mar 2023 22:08:23 GMT, Archie Cobbs <acobbs at openjdk.org> wrote:
>> This bug and a few others fall into the "output file clash" bucket. This is when the compiler thinks it's writing out two separate files, but due to the way the O/S filesystem maps `Path`'s to actual files, it's really writing the same file twice.
>>
>> This is usually due to case-insensitive filesystems, but can also be due to how a filesystem "normalizes" file names. For example, on MacOS, compiling this class will generate such a clash:
>>
>> public class Test {
>> interface Cafe\u0301 {
>> }
>> interface Caf\u00e9 {
>> }
>> }
>>
>> The reason is that `\u0301` is the Unicode character "Combining Acute Accent" which means "stick an accent over the previous character". So MacOS normalizes a `e` followed by a `\u0301` into a Unicode `\u00e9`, that is, `é`. However, the Java language treats these the two names `Cafe\u0301` and `Caf\u00e9` as distinct.
>>
>> It's infeasible to truly "fix" this problem, so we resort here to a salve, which is to add a new compiler flag `--detect-output-file-clashes` that enables detection of output file clashes. When the flag is enabled, and a clash is detected, an error is immediately thrown. For example, compiling the example above gives this:
>>
>> $ javac --help-extra
>> ...
>> --detect-output-file-clashes
>> Generate an error if any output file is overwritten during compilation. This can occur, for example,
>> on case-insensitive filesystems. This applies to class files, native header files, and source files.
>> ...
>> $ javac --detect-output-file-clashes Test.java
>> Test.java:4: error: error while writing Café: output file clash: /Users/archie/test/Test$Café.class
>> interface Caf\u00e9 {
>> ^
>> 1 error
>>
>> This at least gives people at risk of encountering this problem a way to turn a runtime error into a compile-time error.
>>
>> **Outstanding Questions**
>> * Is making this optional via an (extended) flag `--detect-output-file-clashes` the best way to address this?
>> * Does the new field `BaseFileManager.outputFilesWritten` ever need to be cleared? Can the file manager be used for multiple compilations? If so, is there some "reset" step we should hook into?
>
> Archie Cobbs has updated the pull request incrementally with one additional commit since the last revision:
>
> Add "compiler.warn.output.file.clash" to message example exclusion file.
Finished building the jdk from source and tested the fix. It worked!
Here is a demonstration for those who are following along.
$ cat abc.java
public class abc
{
public static class XYZ {}
public static class xyz {}
public static void main(String[] args)
{
System.out.println("abc");
XYZ a = new XYZ();
System.out.println("tuv");
xyz b = new xyz();
System.out.println("xyz");
}
}
$ javac abc.java -Xlint:output-file-clash
warning: [output-file-clash] output file written more than once: C:\Users\david_WORKSPACE_PROGRAMMING_JAVA\abc$xyz.class
1 warning
As you can see, if I use the -Xlint warning above, I now get a warning if any of my classes generate the same filename multiple times. This is perfect, as I now know at compile time that this is something to watch out for.
Thank you for fixing this Archie/Vicente!
-------------
PR Comment: https://git.openjdk.org/jdk/pull/12754#issuecomment-1520466345
More information about the compiler-dev
mailing list