diff options
author | George Fraser <george@fivetran.com> | 2019-03-31 14:09:20 -0700 |
---|---|---|
committer | George Fraser <george@fivetran.com> | 2019-03-31 14:09:20 -0700 |
commit | b9faa90e51312e34e8382d09ad7b1199592bc551 (patch) | |
tree | fd636f7fb8472fa10e2697253d8b3ed677047597 | |
parent | 13b53824ae31f44e0f9fc83eccc352bec8e4939a (diff) | |
download | java-language-server-b9faa90e51312e34e8382d09ad7b1199592bc551.zip |
Re-use compilation tasks
-rw-r--r-- | TODOS.md | 5 | ||||
-rw-r--r-- | lib/extension.ts | 11 | ||||
-rwxr-xr-x | scripts/link_mac.sh | 8 | ||||
-rwxr-xr-x | scripts/link_windows.sh | 13 | ||||
-rw-r--r-- | src/main/java/org/javacs/CompileBatch.java | 52 | ||||
-rw-r--r-- | src/main/java/org/javacs/CompileFile.java | 33 | ||||
-rw-r--r-- | src/main/java/org/javacs/CompileFocus.java | 55 | ||||
-rw-r--r-- | src/main/java/org/javacs/JavaCompilerService.java | 77 | ||||
-rw-r--r-- | src/main/java/org/javacs/JavaLanguageServer.java | 157 | ||||
-rw-r--r-- | src/main/java/org/javacs/ParseFile.java | 24 | ||||
-rw-r--r-- | src/main/java/org/javacs/TaskPool.java (renamed from src/test/java/org/javacs/TaskPool.java) | 0 | ||||
-rw-r--r-- | src/test/java/org/javacs/BenchmarkPruner.java | 4 |
12 files changed, 248 insertions, 191 deletions
@@ -6,4 +6,7 @@ ## Navigation - Go-to-subclasses - Test coverage codelens -- Go-to-implementation for overridden methods
\ No newline at end of file +- Go-to-implementation for overridden methods + +## Bugs +- Imports MyEnum.* unnecessarily
\ No newline at end of file diff --git a/lib/extension.ts b/lib/extension.ts index 3b1a867..68b3f11 100644 --- a/lib/extension.ts +++ b/lib/extension.ts @@ -219,7 +219,7 @@ function platformSpecificLauncher(): string[] { case 'win32': return ['dist', 'windows', 'bin', 'launcher']; - case 'darwin': + case 'darwin': return ['dist', 'mac', 'bin', 'launcher']; } @@ -245,7 +245,14 @@ function visualVmConfig(context: ExtensionContext): ServerOptions { '-Xverify:none', // helps VisualVM avoid 'error 62' '-Xdebug', // '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005', - 'org.javacs.Main' + 'org.javacs.Main', + "--add-exports", "jdk.compiler/com.sun.tools.javac.api=javacs", + "--add-exports", "jdk.compiler/com.sun.tools.javac.code=javacs", + "--add-exports", "jdk.compiler/com.sun.tools.javac.comp=javacs", + "--add-exports", "jdk.compiler/com.sun.tools.javac.main=javacs", + "--add-exports", "jdk.compiler/com.sun.tools.javac.tree=javacs", + "--add-exports", "jdk.compiler/com.sun.tools.javac.model=javacs", + "--add-exports", "jdk.compiler/com.sun.tools.javac.util=javacs", ]; console.log(javaExecutablePath + ' ' + args.join(' ')); diff --git a/scripts/link_mac.sh b/scripts/link_mac.sh index ce29108..72223f2 100755 --- a/scripts/link_mac.sh +++ b/scripts/link_mac.sh @@ -21,4 +21,10 @@ $JAVA_HOME/bin/jlink \ --add-modules gson,javacs \ --launcher launcher=javacs/org.javacs.Main \ --output dist/mac \ - --compress 2
\ No newline at end of file + --compress 2 + +# Restore launcher +echo '#!/bin/sh +JLINK_VM_OPTIONS="--add-exports jdk.compiler/com.sun.tools.javac.api=javacs --add-exports jdk.compiler/com.sun.tools.javac.code=javacs --add-exports jdk.compiler/com.sun.tools.javac.comp=javacs --add-exports jdk.compiler/com.sun.tools.javac.main=javacs --add-exports jdk.compiler/com.sun.tools.javac.tree=javacs --add-exports jdk.compiler/com.sun.tools.javac.model=javacs --add-exports jdk.compiler/com.sun.tools.javac.util=javacs" +DIR=`dirname $0` +$DIR/java $JLINK_VM_OPTIONS -m javacs/org.javacs.Main $@' > dist/mac/bin/launcher
\ No newline at end of file diff --git a/scripts/link_windows.sh b/scripts/link_windows.sh index 3177db5..1f855c9 100755 --- a/scripts/link_windows.sh +++ b/scripts/link_windows.sh @@ -35,4 +35,15 @@ $REAL_JAVA_HOME/bin/jlink \ --add-modules gson,javacs \ --launcher launcher=javacs/org.javacs.Main \ --output dist/windows \ - --compress 2
\ No newline at end of file + --compress 2 + +# Restore launcher +echo '#!/bin/sh +JLINK_VM_OPTIONS="--add-exports jdk.compiler/com.sun.tools.javac.api=javacs --add-exports jdk.compiler/com.sun.tools.javac.code=javacs --add-exports jdk.compiler/com.sun.tools.javac.comp=javacs --add-exports jdk.compiler/com.sun.tools.javac.main=javacs --add-exports jdk.compiler/com.sun.tools.javac.tree=javacs --add-exports jdk.compiler/com.sun.tools.javac.model=javacs --add-exports jdk.compiler/com.sun.tools.javac.util=javacs" +DIR=`dirname $0` +$DIR/java $JLINK_VM_OPTIONS -m javacs/org.javacs.Main $@' > dist/windows/bin/launcher + +echo '@echo off +set JLINK_VM_OPTIONS="--add-exports jdk.compiler/com.sun.tools.javac.api=javacs --add-exports jdk.compiler/com.sun.tools.javac.code=javacs --add-exports jdk.compiler/com.sun.tools.javac.comp=javacs --add-exports jdk.compiler/com.sun.tools.javac.main=javacs --add-exports jdk.compiler/com.sun.tools.javac.tree=javacs --add-exports jdk.compiler/com.sun.tools.javac.model=javacs --add-exports jdk.compiler/com.sun.tools.javac.util=javacs" +set DIR=%~dp0 +"%DIR%\java" %JLINK_VM_OPTIONS% -m javacs/org.javacs.Main %*' > dist/windows/bin/launcher.bat
\ No newline at end of file diff --git a/src/main/java/org/javacs/CompileBatch.java b/src/main/java/org/javacs/CompileBatch.java index de6988c..ddfb505 100644 --- a/src/main/java/org/javacs/CompileBatch.java +++ b/src/main/java/org/javacs/CompileBatch.java @@ -12,10 +12,10 @@ import javax.lang.model.util.*; import javax.tools.*; import org.javacs.lsp.Range; -public class CompileBatch { +public class CompileBatch implements AutoCloseable { private final JavaCompilerService parent; private final ReportProgress progress; - private final JavacTask task; + private final TaskPool.Borrow borrow; private final Trees trees; private final Elements elements; private final Types types; @@ -24,14 +24,14 @@ public class CompileBatch { CompileBatch(JavaCompilerService parent, Collection<? extends JavaFileObject> files, ReportProgress progress) { this.parent = parent; this.progress = progress; - this.task = batchTask(parent, files); - this.trees = Trees.instance(task); - this.elements = task.getElements(); - this.types = task.getTypes(); + this.borrow = batchTask(parent, files); + this.trees = Trees.instance(borrow.task); + this.elements = borrow.task.getElements(); + this.types = borrow.task.getTypes(); this.roots = new ArrayList<CompilationUnitTree>(); // Print timing information for optimization var profiler = new Profiler(); - task.addTaskListener(profiler); + borrow.task.addTaskListener(profiler); // Show progress message through the UI class CountFiles implements TaskListener { Set<URI> parse = new HashSet<>(), enter = new HashSet<>(), analyze = new HashSet<>(); @@ -59,35 +59,39 @@ public class CompileBatch { } } } - task.addTaskListener(new CountFiles()); + borrow.task.addTaskListener(new CountFiles()); // Compile all roots try { - for (var t : task.parse()) roots.add(t); - // The results of task.analyze() are unreliable when errors are present + for (var t : borrow.task.parse()) roots.add(t); + // The results of borrow.task.analyze() are unreliable when errors are present // You can get at `Element` values using `Trees` - task.analyze(); + borrow.task.analyze(); } catch (IOException e) { throw new RuntimeException(e); } profiler.print(); } - static JavacTask batchTask(JavaCompilerService parent, Collection<? extends JavaFileObject> sources) { + @Override + public void close() { + borrow.close(); + } + + static TaskPool.Borrow batchTask(JavaCompilerService parent, Collection<? extends JavaFileObject> sources) { parent.diags.clear(); - return (JavacTask) - parent.compiler.getTask( - null, - parent.fileManager, - parent.diags::add, - JavaCompilerService.options(parent.classPath), - Collections.emptyList(), - sources); + return parent.compiler.getTask( + null, + parent.fileManager, + parent.diags::add, + JavaCompilerService.options(parent.classPath), + Collections.emptyList(), + sources); } public Optional<Element> element(URI uri, int line, int character) { for (var root : roots) { if (root.getSourceFile().toUri().equals(uri)) { - var path = CompileFocus.findPath(task, root, line, character); + var path = CompileFocus.findPath(borrow.task, root, line, character); var el = trees.getElement(path); return Optional.ofNullable(el); } @@ -171,7 +175,7 @@ public class CompileBatch { // Otherwise, scan roots for references List<TreePath> list = new ArrayList<TreePath>(); var map = Map.of(to, list); - var finder = new FindReferences(task); + var finder = new FindReferences(borrow.task); for (var r : roots) { finder.scan(r, map); } @@ -207,7 +211,7 @@ public class CompileBatch { public Index index(URI from, List<Element> declarations) { for (var r : roots) { if (r.getSourceFile().toUri().equals(from)) { - return new Index(task, r, parent.diags, declarations); + return new Index(borrow.task, r, parent.diags, declarations); } } throw new RuntimeException(from + " is not in compiled batch"); @@ -216,7 +220,7 @@ public class CompileBatch { public Optional<Range> range(TreePath path) { var uri = path.getCompilationUnit().getSourceFile().toUri(); var contents = FileStore.contents(uri); - return ParseFile.range(task, contents, path); + return ParseFile.range(borrow.task, contents, path); } private static final Logger LOG = Logger.getLogger("main"); diff --git a/src/main/java/org/javacs/CompileFile.java b/src/main/java/org/javacs/CompileFile.java index 46029ae..bbe7b47 100644 --- a/src/main/java/org/javacs/CompileFile.java +++ b/src/main/java/org/javacs/CompileFile.java @@ -10,11 +10,11 @@ import java.util.logging.Logger; import javax.lang.model.element.*; import org.javacs.lsp.*; -public class CompileFile { +public class CompileFile implements AutoCloseable { private final JavaCompilerService parent; public final URI file; public final String contents; - private final JavacTask task; + private final TaskPool.Borrow borrow; private final Trees trees; public final CompilationUnitTree root; @@ -22,21 +22,26 @@ public class CompileFile { this.parent = parent; this.file = file; this.contents = FileStore.contents(file); - this.task = CompileFocus.singleFileTask(parent, file, contents); - this.trees = Trees.instance(task); + this.borrow = CompileFocus.singleFileTask(parent, file, contents); + this.trees = Trees.instance(borrow.task); var profiler = new Profiler(); - task.addTaskListener(profiler); + borrow.task.addTaskListener(profiler); try { - this.root = task.parse().iterator().next(); - // The results of task.analyze() are unreliable when errors are present + this.root = borrow.task.parse().iterator().next(); + // The results of borrow.task.analyze() are unreliable when errors are present // You can get at `Element` values using `Trees` - task.analyze(); + borrow.task.analyze(); } catch (IOException e) { throw new RuntimeException(e); } profiler.print(); } + @Override + public void close() { + borrow.close(); + } + public SourcePositions sourcePositions() { return trees.getSourcePositions(); } @@ -57,14 +62,14 @@ public class CompileFile { } public Index index(List<Element> declarations) { - return new Index(task, root, parent.diags, declarations); + return new Index(borrow.task, root, parent.diags, declarations); } public Optional<Element> element(int line, int character) { // LOG.info(String.format("Looking for element at %s(%d,%d)...", file.getPath(), line, character)); // First, look for a tree path - var path = CompileFocus.findPath(task, root, line, character); + var path = CompileFocus.findPath(borrow.task, root, line, character); if (path == null) { // LOG.info("...found nothing"); return Optional.empty(); @@ -132,12 +137,12 @@ public class CompileFile { } public Optional<Range> range(TreePath path) { - return ParseFile.range(task, contents, path); + return ParseFile.range(borrow.task, contents, path); } private List<Element> overrides(ExecutableElement method) { - var elements = task.getElements(); - var types = task.getTypes(); + var elements = borrow.task.getElements(); + var types = borrow.task.getTypes(); var results = new ArrayList<Element>(); var enclosingClass = (TypeElement) method.getEnclosingElement(); var enclosingType = enclosingClass.asType(); @@ -219,7 +224,7 @@ public class CompileFile { classes.addAll(parent.classPathClasses); var fixes = Parser.resolveSymbols(unresolved, sourcePathImports, classes); // Figure out which existing imports are actually used - var trees = Trees.instance(task); + var trees = Trees.instance(borrow.task); var references = new HashSet<String>(); class FindUsedImports extends TreePathScanner<Void, Void> { @Override diff --git a/src/main/java/org/javacs/CompileFocus.java b/src/main/java/org/javacs/CompileFocus.java index 38a71d4..c1df941 100644 --- a/src/main/java/org/javacs/CompileFocus.java +++ b/src/main/java/org/javacs/CompileFocus.java @@ -18,14 +18,14 @@ import javax.lang.model.util.Types; import javax.tools.JavaFileObject; import javax.tools.StandardLocation; -public class CompileFocus { +public class CompileFocus implements AutoCloseable { public static final int MAX_COMPLETION_ITEMS = 50; private final JavaCompilerService parent; private final URI file; private final String contents; private final int line, character; - private final JavacTask task; + private final TaskPool.Borrow borrow; private final Trees trees; private final Types types; private final CompilationUnitTree root; @@ -37,35 +37,40 @@ public class CompileFocus { this.contents = Pruner.prune(file, line, character); this.line = line; this.character = character; - this.task = singleFileTask(parent, file, this.contents); - this.trees = Trees.instance(task); - this.types = task.getTypes(); + this.borrow = singleFileTask(parent, file, this.contents); + this.trees = Trees.instance(borrow.task); + this.types = borrow.task.getTypes(); var profiler = new Profiler(); - task.addTaskListener(profiler); + borrow.task.addTaskListener(profiler); try { - this.root = task.parse().iterator().next(); + this.root = borrow.task.parse().iterator().next(); // The results of task.analyze() are unreliable when errors are present // You can get at `Element` values using `Trees` - task.analyze(); + borrow.task.analyze(); } catch (IOException e) { throw new RuntimeException(e); } profiler.print(); - this.path = findPath(task, root, line, character); + this.path = findPath(borrow.task, root, line, character); } + @Override + public void close() { + borrow.close(); + } + + // TODO this is highlighting incorrectly /** Create a task that compiles a single file */ - static JavacTask singleFileTask(JavaCompilerService parent, URI file, String contents) { + static TaskPool.Borrow singleFileTask(JavaCompilerService parent, URI file, String contents) { parent.diags.clear(); - return (JavacTask) - parent.compiler.getTask( - null, - parent.fileManager, - parent.diags::add, - JavaCompilerService.options(parent.classPath), - Collections.emptyList(), - List.of(new SourceFileObject(file, contents))); + return parent.compiler.getTask( + null, + parent.fileManager, + parent.diags::add, + JavaCompilerService.options(parent.classPath), + Collections.emptyList(), + List.of(new SourceFileObject(file, contents))); } /** Find the smallest element that includes the cursor */ @@ -203,8 +208,12 @@ public class CompileFocus { // Get members of switched type var type = trees.getTypeMirror(path); + if (type == null) { + LOG.info("...no type at " + leaf.getExpression()); + return Collections.emptyList(); + } LOG.info(String.format("...switched expression has type `%s`", type)); - var types = task.getTypes(); + var types = borrow.task.getTypes(); var definition = types.asElement(type); if (definition == null) { LOG.info("...type has no definition, completing identifiers instead"); @@ -221,7 +230,7 @@ public class CompileFocus { /** Find all members of expression ending at line:character */ public List<Completion> completeMembers(boolean isReference) { - var types = task.getTypes(); + var types = borrow.task.getTypes(); var scope = trees.getScope(path); var element = trees.getElement(path); @@ -426,7 +435,7 @@ public class CompileFocus { } private void collectSuperMethods(TypeMirror thisType, List<ExecutableElement> result) { - var types = task.getTypes(); + var types = borrow.task.getTypes(); for (var superType : types.directSupertypes(thisType)) { if (superType instanceof DeclaredType) { @@ -472,7 +481,7 @@ public class CompileFocus { collectSupers(t, types); // Object type is not included by default // We need to add it to get members like .equals(other) and .hashCode() - types.add(task.getElements().getTypeElement("java.lang.Object").asType()); + types.add(borrow.task.getElements().getTypeElement("java.lang.Object").asType()); return types; } @@ -514,7 +523,7 @@ public class CompileFocus { /** Find all identifiers in scope at line:character */ List<Element> scopeMembers(String partialName) { - var types = task.getTypes(); + var types = borrow.task.getTypes(); var start = trees.getScope(path); class Walk { diff --git a/src/main/java/org/javacs/JavaCompilerService.java b/src/main/java/org/javacs/JavaCompilerService.java index 8dfeaf8..afa0906 100644 --- a/src/main/java/org/javacs/JavaCompilerService.java +++ b/src/main/java/org/javacs/JavaCompilerService.java @@ -16,7 +16,7 @@ import javax.tools.*; public class JavaCompilerService { // Not modifiable! If you want to edit these, you need to create a new instance final Set<Path> classPath, docPath; - final JavaCompiler compiler = ServiceLoader.load(JavaCompiler.class).iterator().next(); + final TaskPool compiler = new TaskPool(10); final Docs docs; final Set<String> jdkClasses = Classes.jdkTopLevelClasses(), classPathClasses; // Diagnostics from the last compilation task @@ -40,7 +40,6 @@ public class JavaCompilerService { this.docs = new Docs(docPath); this.classPathClasses = Classes.classPathTopLevelClasses(classPath); this.fileManager = new SourceFileManager(); - ; } /** Combine source path or class path entries using the system separator, for example ':' in unix */ @@ -121,46 +120,46 @@ public class JavaCompilerService { // Create task var options = options(classPath); - var task = - (JavacTask) compiler.getTask(null, fileManager, diags::add, options, Collections.emptyList(), sources); - var trees = Trees.instance(task); - - // Print timing information for optimization - var profiler = new Profiler(); - task.addTaskListener(profiler); - - // Run compilation - diags.clear(); - Iterable<? extends CompilationUnitTree> roots; - try { - roots = task.parse(); - task.analyze(); - } catch (IOException e) { - throw new RuntimeException(e); - } - profiler.print(); - LOG.info(String.format("...found %d errors", diags.size())); - - // Check for unused privates - for (var r : roots) { - var warnUnused = new WarnUnused(task); - warnUnused.scan(r, null); - for (var unusedEl : warnUnused.notUsed()) { - var path = trees.getPath(unusedEl); - var message = String.format("`%s` is not used", unusedEl.getSimpleName()); - Diagnostic.Kind kind; - if (unusedEl instanceof ExecutableElement || unusedEl instanceof TypeElement) { - kind = Diagnostic.Kind.OTHER; - } else { - kind = Diagnostic.Kind.WARNING; + try (var borrow = compiler.getTask(null, fileManager, diags::add, options, Collections.emptyList(), sources)) { + var trees = Trees.instance(borrow.task); + + // Print timing information for optimization + var profiler = new Profiler(); + borrow.task.addTaskListener(profiler); + + // Run compilation + diags.clear(); + Iterable<? extends CompilationUnitTree> roots; + try { + roots = borrow.task.parse(); + borrow.task.analyze(); + } catch (IOException e) { + throw new RuntimeException(e); + } + profiler.print(); + LOG.info(String.format("...found %d errors", diags.size())); + + // Check for unused privates + for (var r : roots) { + var warnUnused = new WarnUnused(borrow.task); + warnUnused.scan(r, null); + for (var unusedEl : warnUnused.notUsed()) { + var path = trees.getPath(unusedEl); + var message = String.format("`%s` is not used", unusedEl.getSimpleName()); + Diagnostic.Kind kind; + if (unusedEl instanceof ExecutableElement || unusedEl instanceof TypeElement) { + kind = Diagnostic.Kind.OTHER; + } else { + kind = Diagnostic.Kind.WARNING; + } + diags.add(new Warning(borrow.task, path, kind, "unused", message)); } - diags.add(new Warning(task, path, kind, "unused", message)); } - } - // TODO hint fields that could be final - // TODO hint unused exception + // TODO hint fields that could be final + // TODO hint unused exception - return Collections.unmodifiableList(new ArrayList<>(diags)); + return Collections.unmodifiableList(new ArrayList<>(diags)); + } } public Set<URI> potentialDefinitions(Element to) { diff --git a/src/main/java/org/javacs/JavaLanguageServer.java b/src/main/java/org/javacs/JavaLanguageServer.java index 780d8e5..b1d9894 100644 --- a/src/main/java/org/javacs/JavaLanguageServer.java +++ b/src/main/java/org/javacs/JavaLanguageServer.java @@ -351,35 +351,34 @@ class JavaLanguageServer extends LanguageServer { } // Compile again, focusing on a region that depends on what type of completion we want to do var ctx = maybeCtx.get(); - // TODO CompileFocus should have a "patch" mechanism where we recompile the current file without creating a new - // task - var focus = compiler.compileFocus(uri, ctx.line, ctx.character); - // Do a specific type of completion List<Completion> cs; boolean isIncomplete; - switch (ctx.kind) { - case MemberSelect: - cs = focus.completeMembers(false); - isIncomplete = false; - break; - case MemberReference: - cs = focus.completeMembers(true); - isIncomplete = false; - break; - case Identifier: - cs = focus.completeIdentifiers(ctx.inClass, ctx.inMethod, ctx.partialName); - isIncomplete = cs.size() >= CompileFocus.MAX_COMPLETION_ITEMS; - break; - case Annotation: - cs = focus.completeAnnotations(ctx.partialName); - isIncomplete = cs.size() >= CompileFocus.MAX_COMPLETION_ITEMS; - break; - case Case: - cs = focus.completeCases(); - isIncomplete = false; - break; - default: - throw new RuntimeException("Unexpected completion context " + ctx.kind); + try (var focus = compiler.compileFocus(uri, ctx.line, ctx.character)) { + // Do a specific type of completion + switch (ctx.kind) { + case MemberSelect: + cs = focus.completeMembers(false); + isIncomplete = false; + break; + case MemberReference: + cs = focus.completeMembers(true); + isIncomplete = false; + break; + case Identifier: + cs = focus.completeIdentifiers(ctx.inClass, ctx.inMethod, ctx.partialName); + isIncomplete = cs.size() >= CompileFocus.MAX_COMPLETION_ITEMS; + break; + case Annotation: + cs = focus.completeAnnotations(ctx.partialName); + isIncomplete = cs.size() >= CompileFocus.MAX_COMPLETION_ITEMS; + break; + case Case: + cs = focus.completeCases(); + isIncomplete = false; + break; + default: + throw new RuntimeException("Unexpected completion context " + ctx.kind); + } } // Convert to CompletionItem var result = new ArrayList<CompletionItem>(); @@ -624,6 +623,7 @@ class JavaLanguageServer extends LanguageServer { || !activeFileCache.file.equals(uri) || activeFileCacheVersion != FileStore.version(uri)) { LOG.info("Recompile active file..."); + if (activeFileCache != null) activeFileCache.close(); activeFileCache = compiler.compileFile(uri); activeFileCacheVersion = FileStore.version(uri); } @@ -744,11 +744,10 @@ class JavaLanguageServer extends LanguageServer { if (!FileStore.isJavaFile(uri)) return Optional.empty(); var line = position.position.line + 1; var column = position.position.character + 1; - // TODO CompileFocus should have a "patch" mechanism where we recompile the current file without creating a new - // task - var focus = compiler.compileFocus(uri, line, column); - var help = focus.methodInvocation().map(this::asSignatureHelp); - return help; + try (var focus = compiler.compileFocus(uri, line, column)) { + var help = focus.methodInvocation().map(this::asSignatureHelp); + return help; + } } @Override @@ -770,26 +769,26 @@ class JavaLanguageServer extends LanguageServer { // Compile all files that *might* contain definitions of fromEl var toFiles = compiler.potentialDefinitions(toEl.get()); toFiles.add(fromUri); - var batch = compiler.compileBatch(pruneWord(toFiles, toEl.get())); - - // Find fromEl again, so that we have an Element from the current batch - var fromElAgain = batch.element(fromUri, fromLine, fromColumn).get(); - - // Find all definitions of fromElAgain - var toTreePaths = batch.definitions(fromElAgain); - if (!toTreePaths.isPresent()) return Optional.empty(); - var result = new ArrayList<Location>(); - for (var path : toTreePaths.get()) { - var toUri = path.getCompilationUnit().getSourceFile().toUri(); - var toRange = batch.range(path); - if (!toRange.isPresent()) { - LOG.warning(String.format("Couldn't locate `%s`", path.getLeaf())); - continue; + try (var batch = compiler.compileBatch(pruneWord(toFiles, toEl.get()))) { + // Find fromEl again, so that we have an Element from the current batch + var fromElAgain = batch.element(fromUri, fromLine, fromColumn).get(); + + // Find all definitions of fromElAgain + var toTreePaths = batch.definitions(fromElAgain); + if (!toTreePaths.isPresent()) return Optional.empty(); + var result = new ArrayList<Location>(); + for (var path : toTreePaths.get()) { + var toUri = path.getCompilationUnit().getSourceFile().toUri(); + var toRange = batch.range(path); + if (!toRange.isPresent()) { + LOG.warning(String.format("Couldn't locate `%s`", path.getLeaf())); + continue; + } + var from = new Location(toUri, toRange.get()); + result.add(from); } - var from = new Location(toUri, toRange.get()); - result.add(from); + return Optional.of(result); } - return Optional.of(result); } @Override @@ -811,26 +810,26 @@ class JavaLanguageServer extends LanguageServer { // Compile all files that *might* contain references to toEl var fromUris = compiler.potentialReferences(toEl.get()); fromUris.add(toUri); - var batch = compiler.compileBatch(pruneWord(fromUris, toEl.get())); - - // Find toEl again, so that we have an Element from the current batch - var toElAgain = batch.element(toUri, toLine, toColumn).get(); - - // Find all references to toElAgain - var fromTreePaths = batch.references(toElAgain); - if (!fromTreePaths.isPresent()) return Optional.empty(); - var result = new ArrayList<Location>(); - for (var path : fromTreePaths.get()) { - var fromUri = path.getCompilationUnit().getSourceFile().toUri(); - var fromRange = batch.range(path); - if (!fromRange.isPresent()) { - LOG.warning(String.format("Couldn't locate `%s`", path.getLeaf())); - continue; + try (var batch = compiler.compileBatch(pruneWord(fromUris, toEl.get()))) { + // Find toEl again, so that we have an Element from the current batch + var toElAgain = batch.element(toUri, toLine, toColumn).get(); + + // Find all references to toElAgain + var fromTreePaths = batch.references(toElAgain); + if (!fromTreePaths.isPresent()) return Optional.empty(); + var result = new ArrayList<Location>(); + for (var path : fromTreePaths.get()) { + var fromUri = path.getCompilationUnit().getSourceFile().toUri(); + var fromRange = batch.range(path); + if (!fromRange.isPresent()) { + LOG.warning(String.format("Couldn't locate `%s`", path.getLeaf())); + continue; + } + var from = new Location(fromUri, fromRange.get()); + result.add(from); } - var from = new Location(fromUri, fromRange.get()); - result.add(from); + return Optional.of(result); } - return Optional.of(result); } private List<JavaFileObject> pruneWord(Collection<URI> files, Element el) { @@ -1138,18 +1137,18 @@ class JavaLanguageServer extends LanguageServer { if (!outOfDate.isEmpty()) { // Compile all files that need to be updated in a batch outOfDate.add(toUri); - var batch = compiler.compileBatch(outOfDate); - - // Find all declarations in toFile - var toEls = batch.declarations(toUri); + try (var batch = compiler.compileBatch(outOfDate)) { + // Find all declarations in toFile + var toEls = batch.declarations(toUri); - // Index outOfDate - LOG.info( - String.format( - "...search for references to %d elements in %d files", toEls.size(), outOfDate.size())); - for (var fromUri : outOfDate) { - var index = batch.index(fromUri, toEls); - cacheIndex.put(fromUri, index); + // Index outOfDate + LOG.info( + String.format( + "...search for references to %d elements in %d files", toEls.size(), outOfDate.size())); + for (var fromUri : outOfDate) { + var index = batch.index(fromUri, toEls); + cacheIndex.put(fromUri, index); + } } } else { LOG.info("...all indexes are cached and up-to-date"); diff --git a/src/main/java/org/javacs/ParseFile.java b/src/main/java/org/javacs/ParseFile.java index 33521ed..fe83637 100644 --- a/src/main/java/org/javacs/ParseFile.java +++ b/src/main/java/org/javacs/ParseFile.java @@ -5,15 +5,29 @@ import com.sun.source.tree.*; import com.sun.source.util.*; import java.io.IOException; import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.logging.Logger; import javax.lang.model.element.*; +import javax.tools.JavaCompiler; import org.javacs.lsp.*; public class ParseFile { + private static final JavaCompiler COMPILER = ServiceLoader.load(JavaCompiler.class).iterator().next(); + + /** Create a task that compiles a single file */ + static JavacTask singleFileTask(JavaCompilerService parent, URI file, String contents) { + // TODO could eliminate the connection to parent + parent.diags.clear(); + return (JavacTask) + COMPILER.getTask( + null, + parent.fileManager, + parent.diags::add, + JavaCompilerService.options(parent.classPath), + Collections.emptyList(), + List.of(new SourceFileObject(file, contents))); + } + private final String contents; private final JavacTask task; private final Trees trees; @@ -24,7 +38,7 @@ public class ParseFile { Objects.requireNonNull(file); this.contents = FileStore.contents(file); - this.task = CompileFocus.singleFileTask(parent, file, contents); + this.task = singleFileTask(parent, file, contents); this.trees = Trees.instance(task); var profiler = new Profiler(); task.addTaskListener(profiler); diff --git a/src/test/java/org/javacs/TaskPool.java b/src/main/java/org/javacs/TaskPool.java index 8475ae9..8475ae9 100644 --- a/src/test/java/org/javacs/TaskPool.java +++ b/src/main/java/org/javacs/TaskPool.java diff --git a/src/test/java/org/javacs/BenchmarkPruner.java b/src/test/java/org/javacs/BenchmarkPruner.java index a44d687..16c0c4c 100644 --- a/src/test/java/org/javacs/BenchmarkPruner.java +++ b/src/test/java/org/javacs/BenchmarkPruner.java @@ -51,12 +51,12 @@ public class BenchmarkPruner { @Benchmark public void pruned(CompilerState state) { - state.compiler.compileBatch(List.of(state.pruned)); + state.compiler.compileBatch(List.of(state.pruned)).close(); } @Benchmark public void plain(CompilerState state) { - state.compiler.compileBatch(List.of(state.file)); + state.compiler.compileBatch(List.of(state.file)).close(); } private static final Logger LOG = Logger.getLogger("main"); |