From c2200756518aa960106fd7ac87d0af27a07e4641 Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Wed, 8 Jan 2020 19:14:08 -0500 Subject: [PATCH] WIP 2 --- .../boot/java/utils/CompilationUnitCache.java | 252 ++++++++++++------ .../boot/java/utils/test/AstParserTest.java | 142 +++++----- 2 files changed, 246 insertions(+), 148 deletions(-) diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CompilationUnitCache.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CompilationUnitCache.java index fb72524f9..17cba1f8a 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CompilationUnitCache.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CompilationUnitCache.java @@ -12,11 +12,12 @@ package org.springframework.ide.vscode.boot.java.utils; import java.io.File; import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; @@ -25,17 +26,12 @@ import java.util.function.Function; import java.util.stream.Stream; import org.apache.commons.io.IOUtils; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath; -import org.eclipse.jdt.internal.core.BasicCompilationUnit; -import org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner; -import org.eclipse.jdt.internal.core.INameEnvironmentWithProgress; +import org.eclipse.lsp4j.TextDocumentIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.java.IClasspath; @@ -52,8 +48,6 @@ import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import reactor.core.scheduler.Schedulers; -import reactor.util.function.Tuple2; -import reactor.util.function.Tuples; public final class CompilationUnitCache implements DocumentContentProvider { @@ -64,13 +58,15 @@ public final class CompilationUnitCache implements DocumentContentProvider { private ProjectObserver projectObserver; private Cache uriToCu; private Cache> projectToDocs; - private Cache, INameEnvironmentWithProgress>> lookupEnvCache; + private Cache> lookupEnvCache; private ProjectObserver.Listener projectListener; private SimpleTextDocumentService documents; private AsyncRunner async; private ReadLock readLock; private WriteLock writeLock; + + private ClasspathLookupEnvironmentPool classpathLookupPool = new ClasspathLookupEnvironmentPool(); public CompilationUnitCache(JavaProjectFinder projectFinder, SimpleLanguageServer server, ProjectObserver projectObserver) { this.projectFinder = projectFinder; @@ -81,6 +77,7 @@ public final class CompilationUnitCache implements DocumentContentProvider { // accessed after some time uriToCu = CacheBuilder.newBuilder() .expireAfterWrite(CU_ACCESS_EXPIRATION, TimeUnit.MINUTES) + .removalListener(removal -> invalidateCuForJavaFile(removal.getKey().toString())) .build(); projectToDocs = CacheBuilder.newBuilder().build(); @@ -93,20 +90,34 @@ public final class CompilationUnitCache implements DocumentContentProvider { if (documents != null) { - documents.onDidChangeContent(doc -> invalidateCuForJavaFile(doc.getDocument().getId().getUri())); - documents.onDidClose(doc -> invalidateCuForJavaFile(doc.getId().getUri())); + documents.onDidChangeContent(doc -> { + writeLock.lock(); + try { + invalidateCuForJavaFile(doc.getDocument().getId().getUri()); + } finally { + writeLock.unlock(); + } + }); + documents.onDidClose(doc -> { + writeLock.lock(); + try { + invalidateCuForJavaFile(doc.getId().getUri()); + } finally { + writeLock.unlock(); + } + }); } - async.execute(() -> { - writeLock.lock(); - try { - for (IJavaProject project : projectFinder.all()) { - loadLookupEnvTuple(project); - } - } finally { - writeLock.unlock(); - } - }); +// async.execute(() -> { +// writeLock.lock(); +// try { +// for (IJavaProject project : projectFinder.all()) { +// loadLookupEnvTuple(project); +// } +// } finally { +// writeLock.unlock(); +// } +// }); projectListener = new ProjectObserver.Listener() { @@ -130,8 +141,6 @@ public final class CompilationUnitCache implements DocumentContentProvider { writeLock.lock(); try { invalidateProject(project); - // Load the new cache the value right away - loadLookupEnvTuple(project); } finally { writeLock.unlock(); } @@ -145,8 +154,6 @@ public final class CompilationUnitCache implements DocumentContentProvider { writeLock.lock(); try { invalidateProject(project); - // Load the new cache the value right away - loadLookupEnvTuple(project); } finally { writeLock.unlock(); } @@ -189,10 +196,13 @@ public final class CompilationUnitCache implements DocumentContentProvider { try { cu = uriToCu.get(uri, () -> { - Tuple2, INameEnvironmentWithProgress> lookupEnvTuple = loadLookupEnvTuple(project); + List lookupEnv =lookupEnvCache.get(project, () -> { + return createClasspath(project); + }); String utiStr = uri.toString(); String unitName = utiStr.substring(utiStr.lastIndexOf("/")); - CompilationUnit cUnit = parse2(fetchContent(uri).toCharArray(), utiStr, unitName, lookupEnvTuple.getT1(), lookupEnvTuple.getT2()); + CompilationUnit cUnit = parse2(fetchContent(uri).toCharArray(), utiStr, unitName, lookupEnv, getClasspathEntries(project)); + projectToDocs.get(project, () -> new HashSet<>()).add(uri); return cUnit; }); @@ -226,6 +236,16 @@ public final class CompilationUnitCache implements DocumentContentProvider { writeLock.lock(); try { uriToCu.invalidate(uri); + IJavaProject project = projectFinder.find(new TextDocumentIdentifier(uriStr)).orElse(null); + if (project != null) { + Set docUris = projectToDocs.getIfPresent(project); + if (docUris != null) { + docUris.remove(uri); + if (docUris.isEmpty()) { + invalidateProject(project); + } + } + } } finally { writeLock.unlock(); } @@ -264,62 +284,96 @@ public final class CompilationUnitCache implements DocumentContentProvider { // CompilationUnit cu = (CompilationUnit) parser.createAST(null); // // return cu; +// } +// +// public static CompilationUnit parse2(char[] source, String docURI, String unitName, IJavaProject project) throws Exception { +// List classpaths = createClasspath(project); +// return parse2(source, docURI, unitName, classpaths, getClasspathEntries(project)); // } - public static CompilationUnit parse2(char[] source, String docURI, String unitName, IJavaProject project) throws Exception { - List classpaths = createClasspath(getClasspathEntries(project)); - return parse2(source, docURI, unitName, classpaths, null); - } - - private static CompilationUnit parse2(char[] source, String docURI, String unitName, List classpaths, INameEnvironmentWithProgress environment) throws Exception { + private static CompilationUnit parse2(char[] source, String docURI, String unitName, List classpaths, String[] entries) throws Exception { + ASTParser parser = ASTParser.newParser(AST.JLS11); Map options = JavaCore.getOptions(); - String apiLevel = JavaCore.VERSION_11; - JavaCore.setComplianceOptions(apiLevel, options); - if (environment == null) { - environment = CUResolver.createLookupEnvironment(classpaths.toArray(new Classpath[classpaths.size()])); - } - - BasicCompilationUnit sourceUnit = new BasicCompilationUnit(source, null, unitName, (IJavaElement) null); - - int flags = 0; - boolean needToResolveBindings = true; - flags |= ICompilationUnit.ENABLE_STATEMENTS_RECOVERY; - flags |= ICompilationUnit.ENABLE_BINDINGS_RECOVERY; - CompilationUnitDeclaration unit = null; - try { - unit = CUResolver.resolve(sourceUnit, classpaths, options, flags, environment); - } catch (Exception e) { - flags &= ~ICompilationUnit.ENABLE_BINDINGS_RECOVERY; - unit = CUResolver.parse(sourceUnit, options, flags); - needToResolveBindings = false; - } - - CompilationUnit cu = CUResolver.convert(unit, source, AST.JLS11, options, needToResolveBindings, DefaultWorkingCopyOwner.PRIMARY, flags); - -// classpaths.forEach(e -> e.reset()); - environment.cleanup(); + JavaCore.setComplianceOptions(JavaCore.VERSION_11, options); + parser.setCompilerOptions(options); + parser.setKind(ASTParser.K_COMPILATION_UNIT); + parser.setStatementsRecovery(true); + parser.setBindingsRecovery(true); + parser.setResolveBindings(true); + +// parser.setEnvironment(entries, new String[0], null, false); + parser.setClasspathLookupEnvironment(classpaths); + + parser.setUnitName(unitName); + parser.setSource(source); + + CompilationUnit cu = (CompilationUnit) parser.createAST(null); return cu; + +// Map options = JavaCore.getOptions(); +// String apiLevel = JavaCore.VERSION_11; +// JavaCore.setComplianceOptions(apiLevel, options); +// if (environment == null) { +// environment = CUResolver.createLookupEnvironment(classpaths.toArray(new Classpath[classpaths.size()])); +// } +// +// BasicCompilationUnit sourceUnit = new BasicCompilationUnit(source, null, unitName, (IJavaElement) null); +// +// int flags = 0; +// boolean needToResolveBindings = true; +// flags |= ICompilationUnit.ENABLE_STATEMENTS_RECOVERY; +// flags |= ICompilationUnit.ENABLE_BINDINGS_RECOVERY; +// CompilationUnitDeclaration unit = null; +// try { +// unit = CUResolver.resolve(sourceUnit, classpaths, options, flags, environment); +// } catch (Exception e) { +// flags &= ~ICompilationUnit.ENABLE_BINDINGS_RECOVERY; +// unit = CUResolver.parse(sourceUnit, options, flags); +// needToResolveBindings = false; +// } +// +// CompilationUnit cu = CUResolver.convert(unit, source, AST.JLS11, options, needToResolveBindings, DefaultWorkingCopyOwner.PRIMARY, flags); +// +// return cu; } - private static List createClasspath(String[] classpathEntries) { - ASTParser parser = ASTParser.newParser(AST.JLS11); - String[] sourceEntries = new String[] {}; - parser.setEnvironment(classpathEntries, sourceEntries, null, false); - return CUResolver.getClasspath(parser); - } - - private Tuple2, INameEnvironmentWithProgress> loadLookupEnvTuple(IJavaProject project) { - try { - return lookupEnvCache.get(project, () -> { - List classpaths = createClasspath(getClasspathEntries(project)); - INameEnvironmentWithProgress environment = CUResolver.createLookupEnvironment(classpaths.toArray(new Classpath[classpaths.size()])); - return Tuples.of(classpaths, environment); - }); - } catch (ExecutionException e) { - logger.error("{}", e); - return null; +// private static List createClasspath(String[] classpathEntries) { +// ASTParser parser = ASTParser.newParser(AST.JLS11); +// String[] sourceEntries = new String[] {}; +// parser.setEnvironment(classpathEntries, sourceEntries, null, false); +// return CUResolver.getClasspath(parser); +// } +// +// private List loadLookupEnvTuple(IJavaProject project) { +//// return createClasspath(project); +// try { +// return lookupEnvCache.get(project, () -> { +// return createClasspath(project); +// }); +// } catch (ExecutionException e) { +// logger.error("{}", e); +// return null; +// } +// } +// + private List createClasspath(IJavaProject project) { + IClasspath cp = project.getClasspath(); + List allClasspaths = new ArrayList<>(); + for (File f : IClasspathUtil.getAllBinaryRoots(cp)) { + allClasspaths.addAll(classpathLookupPool.acquire(f.getAbsolutePath())); } + return allClasspaths; + +// ASTParser parser = ASTParser.newParser(AST.JLS11); +// String[] sourceEntries = new String[] {}; +// try { +// parser.setEnvironment(getClasspathEntries(project), sourceEntries, null, false); +// } catch (Exception e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// return CUResolver.getClasspath(parser); } private static String[] getClasspathEntries(IJavaProject project) throws Exception { @@ -340,7 +394,13 @@ public final class CompilationUnitCache implements DocumentContentProvider { uriToCu.invalidateAll(docUris); projectToDocs.invalidate(project); } - lookupEnvCache.invalidate(project); + List paths = lookupEnvCache.getIfPresent(project); + if (paths != null) { + for (File f : IClasspathUtil.getAllBinaryRoots(project.getClasspath())) { + classpathLookupPool.release(f.getAbsolutePath()); + } + lookupEnvCache.invalidate(project); + } } @Override @@ -354,4 +414,42 @@ public final class CompilationUnitCache implements DocumentContentProvider { return IOUtils.toString(uri); } + + private static class ClasspathLookupEnvironmentPool { + + private Map> classpathsMap = new HashMap<>(); + private Map linksMap = new HashMap<>(); + + List acquire(String path) { + List cp = classpathsMap.get(path); + if (cp == null) { + cp = ASTParser.createNonSourceClasspathEntries(path); + classpathsMap.put(path, cp); + linksMap.put(path, 0); + } + linksMap.put(path, linksMap.get(path) + 1); + return cp; + } + + void release(String path) { +// if (!linksMap.containsKey(path) || !classpathsMap.containsKey(path)) { +// throw new IllegalArgumentException("Path doesn't have classpath entries!"); +// } + int links = linksMap.get(path); + if (links <= 1) { + linksMap.remove(path); + List cp = classpathsMap.remove(path); + if (cp != null) { + for (Classpath e : cp) { + e.reset(); + } + } + } else { + if (linksMap.containsKey(path)) { + linksMap.put(path, links - 1); + } + } + } + } + } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/AstParserTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/AstParserTest.java index 5431b57ad..6a6694ee2 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/AstParserTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/AstParserTest.java @@ -48,76 +48,76 @@ public class AstParserTest { assertTrue(jp.getIndex().findType("org.springframework.boot.SpringApplication").exists()); } - @Test - public void test1() throws Exception { - URL sourceUrl = SourceLinks.source(jp, "org.springframework.boot.SpringApplication").get(); - - URI uri = sourceUrl.toURI(); - - String unitName = "SpringApplication"; - - char[] content = IOUtils.toString(uri).toCharArray(); - - CompilationUnit cu = CompilationUnitCache.parse2(content, uri.toString(), unitName, jp); - - assertNotNull(cu); - - cu.accept(new ASTVisitor() { - - @Override - public boolean visit(TypeDeclaration node) { - ITypeBinding binding = node.resolveBinding(); - assertNotNull(binding); - return super.visit(node); - } - - @Override - public boolean visit(SingleMemberAnnotation node) { - IAnnotationBinding annotationBinding = node.resolveAnnotationBinding(); - assertNotNull(annotationBinding); - ITypeBinding binding = node.resolveTypeBinding(); - assertNotNull(binding); - return super.visit(node); - } - - @Override - public boolean visit(NormalAnnotation node) { - IAnnotationBinding annotationBinding = node.resolveAnnotationBinding(); - assertNotNull(annotationBinding); - ITypeBinding binding = node.resolveTypeBinding(); - assertNotNull(binding); - return super.visit(node); - } - - @Override - public boolean visit(MarkerAnnotation node) { - IAnnotationBinding annotationBinding = node.resolveAnnotationBinding(); - assertNotNull(annotationBinding); - ITypeBinding binding = node.resolveTypeBinding(); - assertNotNull(binding); - return super.visit(node); - } - - @Override - public boolean visit(MethodDeclaration node) { - IMethodBinding binding = node.resolveBinding(); - assertNotNull(binding); - if (node.getReturnType2() != null) { - ITypeBinding returnTypeBinding = node.getReturnType2().resolveBinding(); - assertNotNull(returnTypeBinding); - } - return super.visit(node); - } - - @Override - public boolean visit(FieldDeclaration node) { - ITypeBinding binding = node.getType().resolveBinding(); - assertNotNull(binding); - return super.visit(node); - } - - }); - - } +// @Test +// public void test1() throws Exception { +// URL sourceUrl = SourceLinks.source(jp, "org.springframework.boot.SpringApplication").get(); +// +// URI uri = sourceUrl.toURI(); +// +// String unitName = "SpringApplication"; +// +// char[] content = IOUtils.toString(uri).toCharArray(); +// +// CompilationUnit cu = CompilationUnitCache.parse2(content, uri.toString(), unitName, jp); +// +// assertNotNull(cu); +// +// cu.accept(new ASTVisitor() { +// +// @Override +// public boolean visit(TypeDeclaration node) { +// ITypeBinding binding = node.resolveBinding(); +// assertNotNull(binding); +// return super.visit(node); +// } +// +// @Override +// public boolean visit(SingleMemberAnnotation node) { +// IAnnotationBinding annotationBinding = node.resolveAnnotationBinding(); +// assertNotNull(annotationBinding); +// ITypeBinding binding = node.resolveTypeBinding(); +// assertNotNull(binding); +// return super.visit(node); +// } +// +// @Override +// public boolean visit(NormalAnnotation node) { +// IAnnotationBinding annotationBinding = node.resolveAnnotationBinding(); +// assertNotNull(annotationBinding); +// ITypeBinding binding = node.resolveTypeBinding(); +// assertNotNull(binding); +// return super.visit(node); +// } +// +// @Override +// public boolean visit(MarkerAnnotation node) { +// IAnnotationBinding annotationBinding = node.resolveAnnotationBinding(); +// assertNotNull(annotationBinding); +// ITypeBinding binding = node.resolveTypeBinding(); +// assertNotNull(binding); +// return super.visit(node); +// } +// +// @Override +// public boolean visit(MethodDeclaration node) { +// IMethodBinding binding = node.resolveBinding(); +// assertNotNull(binding); +// if (node.getReturnType2() != null) { +// ITypeBinding returnTypeBinding = node.getReturnType2().resolveBinding(); +// assertNotNull(returnTypeBinding); +// } +// return super.visit(node); +// } +// +// @Override +// public boolean visit(FieldDeclaration node) { +// ITypeBinding binding = node.getType().resolveBinding(); +// assertNotNull(binding); +// return super.visit(node); +// } +// +// }); +// +// } }