feat: use custom JavaCompiler implementation for Java LSP

This commit is contained in:
Akash Yadav
2023-02-22 22:59:37 +05:30
parent a4a59af9ed
commit 3d606d16b4
7 changed files with 114 additions and 37 deletions
@@ -0,0 +1,36 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.lsp.java.compiler
import com.itsaky.androidide.javac.services.compiler.ReusableCompiler
import com.itsaky.androidide.javac.services.compiler.ReusableContext
/**
* Implementation of the [ReusableCompiler] which replaces necessary components from the
* [ReusableContext].
*
* @author Akash Yadav
*/
class JCReusableCompiler : ReusableCompiler() {
override fun onCreateContext(): ReusableContext {
return super.onCreateContext().also {
JavaCompilerImpl.preRegister(context = it, replace = true)
}
}
}
@@ -0,0 +1,42 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.lsp.java.compiler
import com.itsaky.androidide.javac.services.compiler.ReusableContext
import com.itsaky.androidide.javac.services.compiler.ReusableJavaCompiler
import jdkx.tools.JavaFileObject
import openjdk.tools.javac.tree.JCTree.JCCompilationUnit
import openjdk.tools.javac.util.Context
class JavaCompilerImpl(context: Context?) : ReusableJavaCompiler(context) {
override fun parse(filename: JavaFileObject?, content: CharSequence?): JCCompilationUnit {
// TODO Prune unnecessary methods to speed up compilation
return super.parse(filename, content)
}
companion object {
@JvmStatic
fun preRegister(context: ReusableContext, replace: Boolean = false) {
if (replace) {
context.drop(compilerKey)
}
context.put(compilerKey, Context.Factory { JavaCompilerImpl(it) })
}
}
}
@@ -86,7 +86,7 @@ public class JavaCompilerService implements CompilerProvider {
protected final SynchronizedTask synchronizedTask = new SynchronizedTask();
protected final SourceFileManager fileManager;
protected final ModuleProject module;
public ReusableCompiler compiler = new ReusableCompiler();
public ReusableCompiler compiler = new JCReusableCompiler();
protected Set<String> bootClasspathClasses =
BootClasspathProvider.getTopLevelClasses(
Collections.singleton(Environment.ANDROID_JAR.getAbsolutePath()));
@@ -156,7 +156,7 @@ public class CompletionProvider extends AbstractServiceProvider implements IComp
final var sourceObject = new SourceFileObject(file);
final var contentBuilder = new StringBuilder(sourceObject.getCharContent(true));
if (!VMUtils.isJvm()) {
if (false && !VMUtils.isJvm()) {
// Cannot use tree sitter in tests
// TODO(itsaky): Should we use the legacy method pruner for tests?
final StopWatch watch = new StopWatch("Prune method bodies");
@@ -59,9 +59,9 @@ import openjdk.tools.javac.util.Position;
*/
public class NBParserFactory extends ParserFactory {
private final ScannerFactory scannerFactory;
private final Names names;
private final CancelService cancelService;
protected final ScannerFactory scannerFactory;
protected final Names names;
protected final CancelService cancelService;
public static void preRegister(Context context) {
context.put(parserFactoryKey, (Context.Factory<ParserFactory>) NBParserFactory::new);
@@ -31,6 +31,10 @@ import com.itsaky.androidide.javac.services.fs.CacheFSInfoSingleton
import com.itsaky.androidide.javac.services.fs.JarPackageProviderImpl
import com.itsaky.androidide.utils.VMUtils
import com.itsaky.androidide.zipfs2.JarPackageProvider
import java.net.URI
import jdkx.tools.DiagnosticListener
import jdkx.tools.JavaFileManager
import jdkx.tools.JavaFileObject
import openjdk.source.util.JavacTask
import openjdk.source.util.TaskEvent
import openjdk.source.util.TaskEvent.Kind.ANALYZE
@@ -53,35 +57,41 @@ import openjdk.tools.javac.util.Context
import openjdk.tools.javac.util.DefinedBy
import openjdk.tools.javac.util.DefinedBy.Api.COMPILER_TREE
import openjdk.tools.javac.util.Log
import java.io.PrintWriter
import java.net.URI
import jdkx.tools.DiagnosticListener
import jdkx.tools.JavaFileManager
import jdkx.tools.JavaFileObject
/**
* Reusable [Context] for [ReusableCompiler].
*
* @author Akash Yadav
*/
class ReusableContext(cancelService: CancelService) : Context(), TaskListener {
private val flowCompleted = mutableSetOf<URI>()
init {
put(Log.logKey, ReusableLog.factory)
put(FSInfo::class.java, if (VMUtils.isJvm()) CacheFSInfo() else CacheFSInfoSingleton)
put(JavaCompiler.compilerKey, ReusableJavaCompiler.factory)
put(JavacFlowListener.flowListenerKey, JavacFlowListener { this.hasFlowCompleted(it) })
put(JarPackageProvider::class.java, JarPackageProviderImpl)
registerNBServices(cancelService)
NBAttr.preRegister(this)
NBParserFactory.preRegister(this)
NBTreeMaker.preRegister(this)
NBJavacTrees.preRegister(this)
NBResolve.preRegister(this)
NBEnter.preRegister(this)
NBMemberEnter.preRegister(this, false)
NBClassFinder.preRegister(this)
NBClassReader.preRegister(this)
CancelService.preRegister(this, cancelService)
}
@DefinedBy(COMPILER_TREE)
override fun started(e: TaskEvent) {
// log.debug("Started: $e")
// Do nothing
}
@DefinedBy(COMPILER_TREE)
override fun finished(e: TaskEvent) {
if (e.kind == ANALYZE) {
@@ -91,7 +101,7 @@ class ReusableContext(cancelService: CancelService) : Context(), TaskListener {
}
}
}
fun clear() {
drop(Arguments.argsKey)
drop(DiagnosticListener::class.java)
@@ -101,7 +111,7 @@ class ReusableContext(cancelService: CancelService) : Context(), TaskListener {
drop(JavacTask::class.java)
drop(JavacTrees::class.java)
drop(JavacElements::class.java)
if (ht[Log.logKey] is ReusableLog) {
// log already init-ed - not first round
(Log.instance(this) as ReusableLog).clear()
@@ -115,15 +125,17 @@ class ReusableContext(cancelService: CancelService) : Context(), TaskListener {
MultiTaskListener.instance(this).clear()
}
}
private fun <T> drop(k: Key<T>?) {
/** **FOR INTERNAL USE ONLY!** */
fun <T> drop(k: Key<T>?) {
ht.remove(k)
}
private fun <T> drop(c: Class<T>?) {
ht.remove(key(c))
/** **FOR INTERNAL USE ONLY!** */
fun <T> drop(c: Class<T>?) {
drop(key(c))
}
private fun hasFlowCompleted(fo: JavaFileObject?): Boolean {
return if (fo == null) {
false
@@ -135,17 +147,4 @@ class ReusableContext(cancelService: CancelService) : Context(), TaskListener {
}
}
}
private fun registerNBServices(cancelService: CancelService) {
NBAttr.preRegister(this)
NBParserFactory.preRegister(this)
NBTreeMaker.preRegister(this)
NBJavacTrees.preRegister(this)
NBResolve.preRegister(this)
NBEnter.preRegister(this)
NBMemberEnter.preRegister(this, false)
NBClassFinder.preRegister(this)
NBClassReader.preRegister(this)
CancelService.preRegister(this, cancelService)
}
}
@@ -25,7 +25,7 @@ import openjdk.tools.javac.util.Context
* Reusable JavaCompiler; exposes a method to clean up the component from leftovers associated
* with previous compilations.
*/
internal class ReusableJavaCompiler(context: Context?) : NBJavaCompiler(context) {
open class ReusableJavaCompiler(context: Context?) : NBJavaCompiler(context) {
companion object {
val factory = Context.Factory<JavaCompiler> { ReusableJavaCompiler(it) }