Implementation(kotlin("compiler")) Breaks Java Compiler

I maintain a fast online Java compilation and execution engine named Jeed that we use to support a playground for my course on introductory programming.

Jeed is actually implemented in Kotlin and I’d like to start teaching in Kotlin at some point in the near future. So I’ve started working on getting in-memory compilation of Kotlin snippets to work. That is going pretty well so far…

…except that I realized that something I had changed had caused all of my Java compilation tests to fail. Turns out it was just adding:

implementation(kotlin("compiler"))

to my build.gradle.kts file. That causes the javax.tools compiler to fail with the following error message:

An exception has occurred in the compiler (10.0.1). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program and the following diagnostic in your report. Thank you.
java.lang.NullPointerException
	at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment$1.visitTopLevel(JavacProcessingEnvironment.java:1528)
	at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCCompilationUnit.accept(JCTree.java:529)
	at jdk.compiler/com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
	at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment$1.scan(JavacProcessingEnvironment.java:1512)
	at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.newRound(JavacProcessingEnvironment.java:1251)
	at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.<init>(JavacProcessingEnvironment.java:1051)
	at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.next(JavacProcessingEnvironment.java:1090)
	at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1322)
	at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1250)
	at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:928)
	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.lambda$doCall$0(JavacTaskImpl.java:100)
	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.handleExceptions(JavacTaskImpl.java:142)
	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:96)
	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:90)
	at edu.illinois.cs.cs125.jeed.core.CompileKt.compile(Compile.kt:78)
	at edu.illinois.cs.cs125.jeed.core.CompileKt.compile$default(Compile.kt:57)
	at edu.illinois.cs.cs125.jeed.core.CompileKt.compile(Compile.kt:107)
	at edu.illinois.cs.cs125.jeed.core.CompileKt.compile$default(Compile.kt:105)
	at edu.illinois.cs.cs125.jeed.core.TestCompile$1$4.invokeSuspend(TestCompile.kt:41)
	at edu.illinois.cs.cs125.jeed.core.TestCompile$1$4.invoke(TestCompile.kt)
	at io.kotlintest.runner.jvm.TestCaseExecutor$executeTest$supervisorJob$1$invokeSuspend$$inlined$map$lambda$1.invokeSuspend(TestCaseExecutor.kt:133)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:233)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1135)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:844)

The error occurs here in JavacProcessingEnvironment.java:1528:

                boolean isModuleInfo = node.sourcefile.isNameCompatible("module-info", Kind.SOURCE);
                if (isModuleInfo) {
                    node.modle.reset();
                    node.modle.completer = sym -> modules.enter(List.of(node), node.modle.module_info);
                    node.modle.module_info.reset(); // This causes a NPE
                    node.modle.module_info.members_field = WriteableScope.create(node.modle.module_info);
                }

I’ve extended the FileManager used by the javax.tools compiler to support memory-backed file objects but also forward other requests to the system file manager. I’ve added some instrumentation there and it looks like something about just importing the Kotlin compiler library modifies the Java environment in such a way that causes Java compilation to fail. The failure seems to occur where the compiler would normally be enumerating the java.io modules, but that’s all I’ve had time to figure out at this point.

The current master branch in the Jeed repository should be able to reproduce the problem. Just add the implementation(kotlin("compiler")) line to core/build.gradle.kts and rerun the tests in TestCompile.kt.

Any pointers would be appreciated—even if it’s just to what part of the compiler package is messing with the Java environment.

Nevermind: I figured out what the problem was. My implementation of isNameCompatible was always returning true, which was causing the Java compiler to try and parse certain in-memory source files as module info files. I have no idea why including the Kotlin compiler triggered this, but fixing it makes the problem go away.