Cache and reuse SourceFileManagers for all modules

This commit is contained in:
Akash Yadav
2022-07-26 23:03:39 +05:30
parent bf1481da79
commit 814eecf565
14 changed files with 130 additions and 91 deletions
@@ -30,7 +30,6 @@ import androidx.annotation.NonNull;
import androidx.core.text.HtmlCompat;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.itsaky.androidide.app.StudioActivity;
import com.itsaky.androidide.databinding.ActivityMainBinding;
import com.itsaky.androidide.fragments.MainFragment;
@@ -121,12 +120,6 @@ public class MainActivity extends StudioActivity {
return binding.getRoot();
}
@Override
protected void onDestroy() {
super.onDestroy();
binding = null;
}
private void showDialogInstallJdkSdk() {
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
builder.setTitle(R.string.title_warning);
@@ -144,11 +137,17 @@ public class MainActivity extends StudioActivity {
builder.show();
}
private void openTerminal() {
startActivity(new Intent(this, TerminalActivity.class));
}
private boolean checkToolsIsInstalled() {
return Environment.JAVA.exists() && Environment.ANDROID_HOME.exists();
}
private void openTerminal() {
startActivity(new Intent(this, TerminalActivity.class));
@Override
protected void onDestroy() {
super.onDestroy();
binding = null;
}
}
+1
View File
@@ -34,6 +34,7 @@ android {
dependencies {
implementation common.kotlin
implementation common.utilcode
implementation google.guava
implementation project (':common')
implementation project(path: ':logger')
@@ -35,6 +35,8 @@
*/
package com.itsaky.androidide.javac.services;
import static com.sun.tools.javac.jvm.ClassFile.Version.V45_3;
import com.sun.tools.javac.code.ClassFinder.BadClassFile;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
@@ -80,7 +82,7 @@ public class NBClassReader extends ClassReader {
NBAttributeReader[] readers = {
new NBAttributeReader(
nbNames._org_netbeans_EnclosingMethod, Version.V45_3, CLASS_OR_MEMBER_ATTRIBUTE) {
nbNames._org_netbeans_EnclosingMethod, V45_3, CLASS_OR_MEMBER_ATTRIBUTE) {
public void read(Symbol sym, int attrLen) {
int newbp = bp + attrLen;
readEnclosingMethodAttr(sym);
@@ -89,7 +91,9 @@ public class NBClassReader extends ClassReader {
},
};
for (NBAttributeReader r : readers) attributeReaders.put(r.getName(), r);
for (NBAttributeReader r : readers) {
attributeReaders.put(r.getName(), r);
}
}
@Override
@@ -166,7 +166,7 @@ object ProjectManager : EventReceiver {
// No-op subscriber method
@Suppress("unused")
@Subscribe(threadMode = MAIN)
fun noOp(obj: Event) {
fun noOp(obj: Throwable) {
throw UnsupportedOperationException("No-op")
}
}
@@ -94,6 +94,10 @@ open class AndroidModule( // Class must be open because BaseXMLTest mocks this..
) :
ModuleProject(name, description, path, projectDir, buildDir, buildScript, tasks, javaCompileOptions), WithModuleData {
companion object {
const val UNKNOWN_PACKAGE = com.itsaky.androidide.tooling.api.model.AndroidModule.UNKNOWN_PACKAGE;
}
private val log = ILogger.newInstance(javaClass.simpleName)
private var cachedLibraries: Map<String, List<DefaultLibrary>> = mutableMapOf()
override var moduleData: SimpleModuleData? = null
@@ -42,7 +42,6 @@ abstract class ModuleProject(
buildDir: File,
buildScript: File,
tasks: List<GradleTask>,
override val compilerSettings: IJavaCompilerSettings
) :
Project(name, description, path, projectDir, buildDir, buildScript, tasks),
@@ -179,4 +178,6 @@ abstract class ModuleProject(
// TODO This can be probably improved
return file.startsWith(this.projectDir.toPath())
}
override fun toString() = "${javaClass.simpleName}: ${this.path}"
}
+1 -1
View File
@@ -10,7 +10,7 @@ ext {
]
androidide = [
javac: 'io.github.itsaky:nb-javac-android:17.0.0.3-SNAPSHOT'
javac: 'io.github.itsaky:nb-javac-android:17.0.0.3'
]
common = [
@@ -29,6 +29,7 @@ import com.itsaky.androidide.lsp.api.IServerSettings;
import com.itsaky.androidide.lsp.internal.model.CachedCompletion;
import com.itsaky.androidide.lsp.java.actions.JavaCodeActionsMenu;
import com.itsaky.androidide.lsp.java.compiler.JavaCompilerService;
import com.itsaky.androidide.lsp.java.compiler.SourceFileManager;
import com.itsaky.androidide.lsp.java.models.JavaServerSettings;
import com.itsaky.androidide.lsp.java.providers.CodeFormatProvider;
import com.itsaky.androidide.lsp.java.providers.CompletionProvider;
@@ -104,7 +105,9 @@ public class JavaLanguageServer implements ILanguageServer {
@Override
public void shutdown() {
JavaCompilerProvider.getInstance().destory();
SourceFileManager.clearCache();
EventBus.getDefault().unregister(this);
timer.shutdown();
}
@@ -135,6 +138,18 @@ public class JavaLanguageServer implements ILanguageServer {
// Once we have project initialized
// Destory the NO_MODULE_COMPILER instance
JavaCompilerService.NO_MODULE_COMPILER.destroy();
// Clear cached file managers
SourceFileManager.clearCache();
// Cache classpath locations
for (final Project subModule : project.getSubModules()) {
if (!(subModule instanceof ModuleProject)) {
continue;
}
SourceFileManager.forModule(((ModuleProject)subModule));
}
}
@NonNull
@@ -207,25 +222,6 @@ public class JavaLanguageServer implements ILanguageServer {
return this.diagnosticProvider.analyze(compiler, file);
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public JavaCompilerService getCompiler(Path file) {
if (!DocumentUtils.isJavaFile(file)) {
return JavaCompilerService.NO_MODULE_COMPILER;
}
final Project root = ProjectManager.INSTANCE.getRootProject();
if (root == null) {
return JavaCompilerService.NO_MODULE_COMPILER;
}
final ModuleProject module = root.findModuleForFile(file);
if (module == null) {
return JavaCompilerService.NO_MODULE_COMPILER;
}
return JavaCompilerProvider.get(module);
}
@NonNull
@Override
public CharSequence formatCode(FormatCodeParams params) {
@@ -248,6 +244,25 @@ public class JavaLanguageServer implements ILanguageServer {
return false;
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public JavaCompilerService getCompiler(Path file) {
if (!DocumentUtils.isJavaFile(file)) {
return JavaCompilerService.NO_MODULE_COMPILER;
}
final Project root = ProjectManager.INSTANCE.getRootProject();
if (root == null) {
return JavaCompilerService.NO_MODULE_COMPILER;
}
final ModuleProject module = root.findModuleForFile(file);
if (module == null) {
return JavaCompilerService.NO_MODULE_COMPILER;
}
return JavaCompilerProvider.get(module);
}
private void updateCachedCompletion(CachedCompletion cachedCompletion) {
Objects.requireNonNull(cachedCompletion);
this.cachedCompletion = cachedCompletion;
@@ -17,8 +17,6 @@
package com.itsaky.androidide.lsp.java.compiler;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
@@ -63,6 +61,7 @@ import java.util.stream.Collectors;
import javax.lang.model.SourceVersion;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;
public class CompileBatch implements AutoCloseable {
@@ -76,7 +75,7 @@ public class CompileBatch implements AutoCloseable {
protected DiagnosticListenerImpl diagnosticListener;
/** Indicates the task that requested the compilation is finished with it. */
boolean closed;
CompileBatch(
JavaCompilerService parent,
Collection<? extends JavaFileObject> files,
@@ -118,7 +117,7 @@ public class CompileBatch implements AutoCloseable {
@NonNull JavaCompilerService parent, @NonNull Collection<? extends JavaFileObject> sources) {
parent.diagnostics.clear();
final Iterable<String> options = options(parent.classPath);
final Iterable<String> options = options();
diagnosticListener =
new DiagnosticListenerWrapper(parent.diagnostics::add, sources.iterator().next());
@@ -129,7 +128,7 @@ public class CompileBatch implements AutoCloseable {
@SuppressWarnings("Since15")
@NonNull
private List<String> options(Set<Path> classPath) {
private List<String> options() {
List<String> options = new ArrayList<>();
// TODO Boot classpath must be different in java projects
@@ -140,17 +139,6 @@ public class CompileBatch implements AutoCloseable {
JavacConfigProvider.setLatestSupportedSourceVersion(SourceVersion.RELEASE_11);
JavacConfigProvider.disableModules();
final List<String> bootClasspaths = getAndUpdateBootclasspaths();
if (!bootClasspaths.isEmpty()) {
Collections.addAll(
options, "-bootclasspath", TextUtils.join(File.pathSeparator, bootClasspaths));
}
if (!classPath.isEmpty()) {
Collections.addAll(options, "-classpath", joinPath(classPath));
}
setupCompileOptions(parent.module, options);
Collections.addAll(options, "-proc:none");
Collections.addAll(options, "-g");
@@ -197,26 +185,6 @@ public class CompileBatch implements AutoCloseable {
options.add(compilerSettings.getJavaBytecodeVersion());
}
private List<String> getAndUpdateBootclasspaths() {
final List<String> bootClasspaths = new ArrayList<>(5);
if (parent.module == null) {
// Use default boot classpath if no module is available
bootClasspaths.add(Environment.ANDROID_JAR.getAbsolutePath());
} else if (parent.module.getType() == IProject.Type.Android) {
bootClasspaths.addAll(
((AndroidModule) parent.module)
.getBootClassPaths().stream().map(File::getPath).collect(Collectors.toSet()));
}
if (BootClasspathProvider.update(bootClasspaths)) {
this.parent.bootClasspathClasses.clear();
this.parent.bootClasspathClasses.addAll(
BootClasspathProvider.getTopLevelClasses(bootClasspaths));
}
return bootClasspaths;
}
/**
* Combine source path or class path entries using the system separator, for example ':' in unix
*/
@@ -74,11 +74,10 @@ import javax.tools.StandardLocation;
public class JavaCompilerService implements CompilerProvider {
public static final JavaCompilerService NO_MODULE_COMPILER = new JavaCompilerService(null);
private static final Cache<String, Boolean> cacheContainsWord = new Cache<>();
private static final Cache<Void, List<String>> cacheContainsType = new Cache<>();
private static final ILogger LOG = ILogger.newInstance("JavaCompilerService");
public static JavaCompilerService NO_MODULE_COMPILER = new JavaCompilerService(null);
protected final Set<Path> classPath;
protected final Set<String> classPathClasses;
protected final List<Diagnostic<? extends JavaFileObject>> diagnostics = new ArrayList<>();
protected final Map<JavaFileObject, Long> cachedModified = new HashMap<>();
@@ -97,14 +96,11 @@ public class JavaCompilerService implements CompilerProvider {
// It is marked as nullable just for some special cases like tests
public JavaCompilerService(@Nullable ModuleProject module) {
this.module = module;
this.fileManager = new SourceFileManager(module);
if (module == null) {
this.classPath = Collections.unmodifiableSet(Collections.emptySet());
this.fileManager = SourceFileManager.NO_MODULE;
this.classPathClasses = Collections.emptySet();
} else {
this.classPath =
Collections.unmodifiableSet(
module.getCompileClasspaths().stream().map(File::toPath).collect(Collectors.toSet()));
this.fileManager = SourceFileManager.forModule(module);
this.classPathClasses = module.compileClasspathClasses.allClassNames();
this.bootClasspathClasses = getBootclasspathClasses();
}
@@ -17,8 +17,14 @@
package com.itsaky.androidide.lsp.java.compiler;
import androidx.annotation.NonNull;
import com.blankj.utilcode.util.CloseUtils;
import com.google.common.collect.Iterables;
import com.itsaky.androidide.projects.api.AndroidModule;
import com.itsaky.androidide.projects.api.ModuleProject;
import com.itsaky.androidide.projects.util.StringSearch;
import com.itsaky.androidide.utils.Environment;
import com.itsaky.androidide.utils.ILogger;
import com.itsaky.androidide.utils.SourceClassTrie;
import com.sun.tools.javac.api.JavacTool;
@@ -29,7 +35,10 @@ import java.nio.charset.Charset;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;
@@ -44,16 +53,34 @@ import javax.tools.StandardLocation;
@SuppressWarnings("Since15")
public class SourceFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
public static final SourceFileManager NO_MODULE = new SourceFileManager(null);
private static final ILogger LOG = ILogger.newInstance("SourceFileManager");
private ModuleProject module;
private static final Map<ModuleProject, SourceFileManager> cachedFileManagers = new HashMap<>();
private final ModuleProject module;
public SourceFileManager() {
this(null);
}
public SourceFileManager(final ModuleProject module) {
private SourceFileManager(final ModuleProject module) {
super(createDelegateFileManager());
this.module = module;
if (module != null) {
setLocation(StandardLocation.CLASS_PATH, module.getCompileClasspaths());
setLocation(StandardLocation.SOURCE_PATH, module.getCompileSourceDirectories());
if (module instanceof AndroidModule) {
final AndroidModule android = ((AndroidModule) module);
setLocation(StandardLocation.PLATFORM_CLASS_PATH, android.getBootClassPaths());
} else {
setFallbackPlatformClasspath();
}
} else {
setFallbackPlatformClasspath();
}
}
protected void setFallbackPlatformClasspath() {
// TODO Module system must be enabled for Java modules instead of setting Platform Classpath
setLocation(
StandardLocation.PLATFORM_CLASS_PATH, Collections.singleton(Environment.ANDROID_JAR));
}
private static StandardJavaFileManager createDelegateFileManager() {
@@ -66,10 +93,32 @@ public class SourceFileManager extends ForwardingJavaFileManager<StandardJavaFil
LOG.warn("SourceFileManager DiagnosticErr:", error.getMessage(null));
}
public void setModule(final ModuleProject module) {
this.module = module;
void setLocation(Location location, Iterable<? extends File> files) {
try {
fileManager.setLocation(location, files);
} catch (IOException e) {
LOG.error("Failed to update location", location, "with files", Iterables.toString(files), e);
}
}
public static void clearCache() {
for (final SourceFileManager fileManager : cachedFileManagers.values()) {
CloseUtils.closeIO(fileManager);
}
cachedFileManagers.clear();
}
public static SourceFileManager forModule(@NonNull ModuleProject project) {
Objects.requireNonNull(project);
return cachedFileManagers.computeIfAbsent(project, SourceFileManager::createForModule);
}
private static SourceFileManager createForModule(@NonNull ModuleProject project) {
LOG.info("Creating source file manager instance for module:", project);
return new SourceFileManager(project);
}
@Override
public Iterable<JavaFileObject> list(
Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse)
@@ -156,10 +205,6 @@ public class SourceFileManager extends ForwardingJavaFileManager<StandardJavaFil
}
}
void setLocation(Location location, Iterable<? extends File> files) throws IOException {
fileManager.setLocation(location, files);
}
void setLocationFromPaths(Location location, Collection<? extends Path> searchpath)
throws IOException {
fileManager.setLocationFromPaths(location, searchpath);
@@ -17,6 +17,7 @@
package com.itsaky.androidide.lsp.java.compiler;
import com.google.common.base.MoreObjects;
import com.itsaky.androidide.lsp.util.PathUtils;
import com.itsaky.androidide.projects.FileManager;
import com.itsaky.androidide.utils.DocumentUtils;
@@ -68,7 +69,7 @@ public class SourceFileObject implements JavaFileObject {
@Override
public String toString() {
return path.toString();
return MoreObjects.toStringHelper(this).add("path", this.path.toString()).toString();
}
@Override
@@ -22,6 +22,7 @@ import com.itsaky.androidide.lsp.java.compiler.SourceFileObject;
import com.itsaky.androidide.models.Position;
import com.itsaky.androidide.models.Range;
import com.itsaky.androidide.projects.ProjectManager;
import com.itsaky.androidide.projects.api.ModuleProject;
import com.itsaky.androidide.utils.ILogger;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
@@ -53,7 +54,7 @@ import javax.tools.JavaFileObject;
public class Parser {
private static final JavaCompiler COMPILER = JavacTool.create();
private static final SourceFileManager FILE_MANAGER = new SourceFileManager();
private static SourceFileManager FILE_MANAGER = SourceFileManager.NO_MODULE;
private static final ILogger LOG = ILogger.newInstance("JavaParser");
private static Parser cachedParse;
private static long cachedModified = -1;
@@ -81,7 +82,11 @@ public class Parser {
/** Create a task that compiles a single file */
private static JavacTask singleFileTask(JavaFileObject file) {
FILE_MANAGER.setModule(ProjectManager.INSTANCE.findModuleForFile(Paths.get(file.toUri())));
final ModuleProject module = ProjectManager.INSTANCE.findModuleForFile(Paths.get(file.toUri()));
if (module != null) {
FILE_MANAGER = SourceFileManager.forModule(module);
}
return (JavacTask)
COMPILER.getTask(
null,
Binary file not shown.