mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Cache and reuse SourceFileManagers for all modules
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ android {
|
||||
dependencies {
|
||||
implementation common.kotlin
|
||||
implementation common.utilcode
|
||||
implementation google.guava
|
||||
implementation project (':common')
|
||||
implementation project(path: ':logger')
|
||||
|
||||
|
||||
+6
-2
@@ -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
|
||||
|
||||
+2
-1
@@ -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
@@ -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
|
||||
*/
|
||||
|
||||
+3
-7
@@ -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();
|
||||
}
|
||||
|
||||
+57
-12
@@ -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);
|
||||
|
||||
+2
-1
@@ -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.
Reference in New Issue
Block a user