Potential bugs when using the JavaCompiler API.

Guoxiong Li lgxbslgx at gmail.com
Fri Dec 18 14:38:52 UTC 2020


Hi all,

When I was working on the JDK-8232939[1], I found some strange points and
suspicious bugs. The origin of the issues is the `Context` of
`StandardJavaFileManager`(eg: JavacFileManager) and the `Context` of
`CompilationTask`(eg: JavacTaskImpl) is not same. As we can see in class
`JavacTool`, the methods `getStandardFileManager` and `getTask` both create
its new `Context`.

When creating its new `Context` to isolate. We can call it as the
*decoupled* part of `StandardJavaFileManager` and `CompilationTask`.
When using `getTask` with a file manager to get a `CompilationTask`, the
`getTask` will set some properties of the file manager. We can call it as
the *coupled* part of `StandardJavaFileManager` and `CompilationTask`.

The decoupled and coupled part of `StandardJavaFileManager` and
`CompilationTask` are strange and both have suspicious bugs which are shown
below.

1. Bugs about the decoupled part.
    The properties of `JavacFileManager`, especially  `Log` and `Options`,
are not same as the corresponding items of `JavacTaskImpl`. Some issues
will occur.

    The first example is that the OptionGroup.FILEMANAGER options won't be
validated. This is a bug about the different `Options`.
    As you can see the test code[2]. `--module-source-path` and
`--source-path` are both set. It is wrong because the man document states
that users can use only one of `--module-source-path` and `--source-path`,
and can't use both. These wrong code are accepted by the compiler now.
Because when using the compiler API, the options property of file manager
only contain the OptionGroup.FILEMANAGER options and the options property
of compiler task only contain the OptionGroup.BASIC options. When
validating options[3], only the options of compiler task will be validated.
That is said, the OptionGroup.FILEMANAGER options won't be validated. It is
unacceptable because the manner of compiler API is different from the
compiler command line.

    The second example is JDK-8232939[1]. It is a bug about the different
`Log.`
    The file manager generates the errors during the parse stage and
reports the error by using its own `Log`. After the parse stage, the
compiler uses `stopIfError` to check if the errors occur and will stop the
later stages if any error occurs. Unfortunately, the method `stopIfError`
checks the `Log` of the compiler instead of the `Log` of the file manager
so that it can't find any error generated by the file manager. So the
compiler continues the later stages unexpectedly.

    These two bugs(one about `Options`, another about `Log`) show that
current decoupled way is not so good. If we deep into all the properties of
these two different context, some more bugs may be found.

2. Bugs about the coupled way.
    This aspect are more important and more unacceptable. As you can see
the code snippet below.
        ```
        var compiler = ToolProvider.getSystemJavaCompiler();
        var fm = compiler.getStandardFileManager(null, null, null);
        var files = fm.getJavaFileObjects(Path.of("Test.java"));
        JavaCompiler.CompilationTask task1 = compiler.getTask(null, fm,
null, List.of("-XDrawDiagnostics", "-encoding", "ASCII"), null, files);
        JavaCompiler.CompilationTask task2 = compiler.getTask(null, fm,
null, List.of("-XDrawDiagnostics"/*some more options*/), null, files);
        System.out.println(task1.call());
        System.out.println(task2.call());
        ```
    The OptionGroup.FILEMANAGER options of `task1`("-encoding", "ASCII")
will be taken to `task2` and  vice versa. Because the options of file
manager won't be cleared. It required the users clear the options manually.
Unfortunately, the users, included me, use it incorrectly. The probably
correct usage is shown below: run the first task and clear the information
of previous task before getting another task. As you can see, the clear
work is hard because the users don't know the internal of the compiler.
Currently I can't find a good way to clear, too.
        ```
        var compiler = ToolProvider.getSystemJavaCompiler();
        var fm = compiler.getStandardFileManager(null, null, null);
        var files = fm.getJavaFileObjects(Path.of("Test.java"));
        JavaCompiler.CompilationTask task1 = compiler.getTask(null, fm,
null, List.of("-XDrawDiagnostics", "-encoding", "ASCII"), null, files);
        System.out.println(task1.call());
        // Do some clear work. Omit.
        JavaCompiler.CompilationTask task2 = compiler.getTask(null, fm,
null, List.of("-XDrawDiagnostics"), null, files);
        System.out.println(task2.call());
        ```
    As the document stated:
    > The standard file manager serves two purposes:
    >     basic building block for customizing how a compiler reads and
writes files
    >     sharing between multiple compilation tasks
    But the options and task's own individual other information perhaps
shouldn't be shared.


To solve the problem completely, we should find a better way to
decouple file manager and compilation task instead of using the new
`Context` directly and simply.
I have been thinking of a better solution these days but I can't find a
satisfying solution. I want to share the problem with you to see your
opinions. This is the reason of this email.

Any opinion or idea are appreciated!

[1] https://bugs.openjdk.java.net/browse/JDK-8232939
[2]
https://github.com/openjdk/jdk/blob/853c04712d24788b6d30d54443ac9277a898311b/test/langtools/tools/javac/modules/AnnotationProcessing.java#L1607
[3]
https://github.com/openjdk/jdk/blob/853c04712d24788b6d30d54443ac9277a898311b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java#L409
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20201218/ea5bb476/attachment.htm>


More information about the compiler-dev mailing list