WIP Batch compiler

This commit is contained in:
aboyko
2023-04-11 12:15:53 -04:00
parent ebc2b461d3
commit fd90136a8e
8 changed files with 353 additions and 0 deletions

View File

@@ -20,9 +20,12 @@ import java.util.stream.Stream;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.utils.CuDeclarationUtils;
import org.springframework.ide.vscode.commons.util.CollectorUtil;
import com.google.common.collect.ImmutableList;
@@ -74,6 +77,32 @@ public abstract class AnnotationHierarchies {
}
}
public static Collection<TypeBinding> getDirectSuperAnnotations2(TypeBinding typeBinding) {
synchronized(lock) {
try {
AnnotationBinding[] annotations = typeBinding.getAnnotations();
if (annotations != null && annotations.length != 0) {
ImmutableList.Builder<TypeBinding> superAnnotations = ImmutableList.builder();
for (AnnotationBinding ab : annotations) {
TypeBinding sa = ab.getAnnotationType();
if (sa != null) {
if (!ignoreAnnotation(CuDeclarationUtils.getQualifiedName(sa))) {
superAnnotations.add(sa);
}
}
}
return superAnnotations.build();
}
}
catch (AbortCompilation e) {
log.debug("compilation aborted ", e);
// ignore this, it is most likely caused by broken source code, a broken classpath, or some optional dependencies not being on the classpath
}
return ImmutableList.of();
}
}
public static Set<String> getTransitiveSuperAnnotations(ITypeBinding typeBinding) {
synchronized(lock) {
Set<String> seen = new HashSet<>();
@@ -81,6 +110,15 @@ public abstract class AnnotationHierarchies {
return seen;
}
}
public static Set<String> getTransitiveSuperAnnotations2(TypeBinding typeBinding) {
synchronized(lock) {
Set<String> seen = new HashSet<>();
findTransitiveSupers2(typeBinding, seen).collect(Collectors.toList());
return seen;
}
}
public static Stream<ITypeBinding> findTransitiveSupers(ITypeBinding typeBinding, Set<String> seen) {
synchronized(lock) {
@@ -97,6 +135,21 @@ public abstract class AnnotationHierarchies {
}
}
public static Stream<TypeBinding> findTransitiveSupers2(TypeBinding typeBinding, Set<String> seen) {
synchronized(lock) {
String qname = CuDeclarationUtils.getQualifiedName(typeBinding);
if (seen.add(qname)) {
return Stream.concat(
Stream.of(typeBinding),
getDirectSuperAnnotations2(typeBinding).stream().flatMap(superBinding ->
findTransitiveSupers2(superBinding, seen)
)
);
}
return Stream.empty();
}
}
public static boolean isSubtypeOf(Annotation annotation, String fqAnnotationTypeName) {
synchronized(lock) {
ITypeBinding annotationType = annotation.resolveTypeBinding();

View File

@@ -27,6 +27,9 @@ import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.HoverParams;
@@ -99,6 +102,35 @@ public class BootJavaHoverProvider implements HoverHandler {
if (processLiveData.length == 0) return new CodeLens[0];
if (project == null) return new CodeLens[0];
server.getCompilationUnitCache().withCompilationUnitDeclaration(project, URI.create(document.getUri()), cu -> {
Collection<CodeLens> result = new LinkedHashSet<>();
cu.traverse(new org.eclipse.jdt.internal.compiler.ASTVisitor() {
@Override
public boolean visit(org.eclipse.jdt.internal.compiler.ast.TypeDeclaration localTypeDeclaration,
BlockScope scope) {
extractLiveHintsForType2(localTypeDeclaration, document, project, processLiveData, result);
return super.visit(localTypeDeclaration, scope);
}
@Override
public boolean visit(org.eclipse.jdt.internal.compiler.ast.TypeDeclaration memberTypeDeclaration,
ClassScope scope) {
extractLiveHintsForType2(memberTypeDeclaration, document, project, processLiveData, result);
return super.visit(memberTypeDeclaration, scope);
}
@Override
public boolean visit(org.eclipse.jdt.internal.compiler.ast.TypeDeclaration typeDeclaration,
CompilationUnitScope scope) {
extractLiveHintsForType2(typeDeclaration, document, project, processLiveData, result);
return super.visit(typeDeclaration, scope);
}
}, (BlockScope) null);
return result;
});
return server.getCompilationUnitCache().withCompilationUnit(project, URI.create(document.getUri()), cu -> {
Collection<CodeLens> result = new LinkedHashSet<>();
@@ -193,6 +225,17 @@ public class BootJavaHoverProvider implements HoverHandler {
}
}
protected void extractLiveHintsForType2(org.eclipse.jdt.internal.compiler.ast.TypeDeclaration typeDeclaration, TextDocument doc, IJavaProject project,
SpringProcessLiveData[] processLiveData, Collection<CodeLens> result) {
Collection<HoverProvider> providers = this.hoverProviders.getAll();
for (HoverProvider provider : providers) {
Collection<CodeLens> hints = provider.getLiveHintCodeLenses2(project, typeDeclaration, doc, processLiveData);
if (hints!=null) {
result.addAll(hints);
}
}
}
protected void extractLiveHintsForAnnotation(Annotation annotation, TextDocument doc, IJavaProject project,
SpringProcessLiveData[] processLiveData, Collection<CodeLens> result) {
ITypeBinding type = annotation.resolveTypeBinding();

View File

@@ -56,5 +56,9 @@ public interface HoverProvider {
default Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, MethodDeclaration methodDeclaration, TextDocument doc, SpringProcessLiveData[] processLiveData) {
return null;
}
default Collection<CodeLens> getLiveHintCodeLenses2(IJavaProject project, org.eclipse.jdt.internal.compiler.ast.TypeDeclaration typeDeclaration, TextDocument doc, SpringProcessLiveData[] processLiveData) {
return null;
}
}

View File

@@ -103,6 +103,59 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
return null;
}
protected List<CodeLens> assembleCodeLenses2(IJavaProject project, SpringProcessLiveData[] processLiveData, DefinedBeanProvider definedBeanProvider,
TextDocument doc, Range range, org.eclipse.jdt.internal.compiler.ast.ASTNode node) {
List<CodeLens> codeLenses = null;
boolean beanFound = false;
for (SpringProcessLiveData liveData : processLiveData) {
LiveBean definedBean = definedBeanProvider.definedBean(liveData);
if (definedBean == null) {
continue;
}
beanFound = true;
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(liveData, definedBean);
if (!relevantBeans.isEmpty()) {
List<LiveBean> injectedBeans = getRelevantInjectedIntoBeans(project, liveData, definedBean, relevantBeans);
ImmutableList.Builder<CodeLens> builder = ImmutableList.builder();
if (!injectedBeans.isEmpty()) {
// Break out of the loop. Just look for the first app with injected into beans
List<CodeLens> injectedCodeLenses = LiveHoverUtils.createCodeLensesForBeans(range, injectedBeans,
BEANS_PREFIX_PLAIN_TEXT, MAX_INLINE_BEANS_STRING_LENGTH, INLINE_BEANS_STRING_SEPARATOR);
builder.addAll(
injectedCodeLenses.isEmpty() ? ImmutableList.of(new CodeLens(range)) : injectedCodeLenses);
}
// Wired beans code lenses
List<LiveBean> wiredBeans = findWiredBeans2(project, liveData, relevantBeans, node);
builder.addAll(assembleCodeLenseForAutowired2(wiredBeans, project, liveData, doc, range, node));
// Startup metrics CodeLens
if (liveData.getStartupMetrics() != null) {
Duration startupTime = liveData.getStartupMetrics().getBeanInstanciationTime(definedBean.getId());
if (startupTime != null) {
builder.add(LiveHoverUtils.createCodeLenseForBeanStartupMetric(range, startupTime));
}
}
// If Injected into and Wired beans are found for an app just return ocde lenses for the bean from the app
codeLenses = builder.build();
break;
} else if (liveData.getStartupMetrics() != null && liveData.getStartupMetrics().getBeanInstanciationTime(definedBean.getId()) != null) {
Duration startupTime = liveData.getStartupMetrics().getBeanInstanciationTime(definedBean.getId());
codeLenses = ImmutableList.of(LiveHoverUtils.createCodeLenseForBeanStartupMetric(range, startupTime));
}
}
if (beanFound) {
return codeLenses == null || codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(range)) : codeLenses;
}
return null;
}
protected List<CodeLens> assembleCodeLenses(IJavaProject project, SpringProcessLiveData[] processLiveData, DefinedBeanProvider definedBeanProvider,
TextDocument doc, Range range, ASTNode node) {
List<CodeLens> codeLenses = null;
@@ -162,10 +215,20 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
INLINE_BEANS_STRING_SEPARATOR);
}
protected List<CodeLens> assembleCodeLenseForAutowired2(List<LiveBean> wiredBeans, IJavaProject project, SpringProcessLiveData processLiveData, TextDocument doc, Range nameRange, org.eclipse.jdt.internal.compiler.ast.ASTNode astNode) {
return LiveHoverUtils.createCodeLensesForBeans(nameRange, wiredBeans,
AutowiredHoverProvider.BEANS_PREFIX_PLAIN_TEXT, MAX_INLINE_BEANS_STRING_LENGTH,
INLINE_BEANS_STRING_SEPARATOR);
}
protected List<LiveBean> findWiredBeans(IJavaProject project, SpringProcessLiveData liveData, List<LiveBean> relevantBeans, ASTNode astNode) {
return Collections.emptyList();
}
protected List<LiveBean> findWiredBeans2(IJavaProject project, SpringProcessLiveData liveData, List<LiveBean> relevantBeans, org.eclipse.jdt.internal.compiler.ast.ASTNode astNode) {
return Collections.emptyList();
}
protected Hover assembleHover(IJavaProject project, SpringProcessLiveData[] processLiveData, DefinedBeanProvider definedBeanProvider, ASTNode astNode, boolean injected, boolean wired) {
StringBuilder hover = new StringBuilder();

View File

@@ -98,6 +98,22 @@ public class BeanInjectedIntoHoverProvider extends AbstractInjectedIntoHoverProv
return Collections.emptyList();
}
//TODO: implement me
@Override
protected List<LiveBean> findWiredBeans2(IJavaProject project, SpringProcessLiveData liveData, List<LiveBean> relevantBeans, org.eclipse.jdt.internal.compiler.ast.ASTNode astNode) {
// if (astNode instanceof org.eclipse.jdt.internal.compiler.ast.Annotation) {
// // @Bean annotation case
// MethodDeclaration beanMethod = ASTUtils.getAnnotatedMethod((org.eclipse.jdt.internal.compiler.ast.Annotation) astNode);
// if (beanMethod != null) {
// return AutowiredHoverProvider.getRelevantAutowiredBeans(project, beanMethod, liveData, relevantBeans);
// }
// } else if (astNode instanceof SingleVariableDeclaration) {
// // Bean method parameter case
// return AutowiredHoverProvider.getRelevantAutowiredBeans(project, astNode, liveData, relevantBeans);
// }
return Collections.emptyList();
}
@Override
protected List<CodeLens> assembleCodeLenseForAutowired(List<LiveBean> wiredBeans, IJavaProject project,
SpringProcessLiveData liveData, TextDocument doc, Range nameRange, ASTNode astNode) {

View File

@@ -22,6 +22,7 @@ import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.Range;
@@ -38,6 +39,7 @@ import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import com.google.common.collect.ImmutableList;
@@ -97,10 +99,28 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
});
}
private static String getBeanId2(Annotation annotation, TypeBinding beanType, boolean isStatic) {
return ASTUtils.getAttribute(annotation, "value").flatMap(ASTUtils::getFirstString).orElseGet(() -> {
if (beanType.enclosingType() == null) {
return BeanUtils.getBeanNameFromType(new String(beanType.shortReadableName()));
} else {
if (isStatic) {
return BeanUtils.getBeanNameFromType(new String(beanType.qualifiedSourceName()));
} else {
return getBeanType2(beanType).toString();
}
}
});
}
private static String getBeanType(ITypeBinding beanType) {
return beanType.getBinaryName();
}
private static String getBeanType2(TypeBinding beanType) {
return new String(beanType.qualifiedSourceName()).replace('.', '$');
}
@Override
public Collection<CodeLens> getLiveHintCodeLenses(IJavaProject project, TypeDeclaration typeDeclaration,
TextDocument doc, SpringProcessLiveData[] processLiveData) {
@@ -165,6 +185,33 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
}
return null;
}
@Override
public Collection<CodeLens> getLiveHintCodeLenses2(IJavaProject project,
org.eclipse.jdt.internal.compiler.ast.TypeDeclaration typeDeclaration, TextDocument doc,
SpringProcessLiveData[] processLiveData) {
if (processLiveData.length > 0 && !isComponentAnnotatedType2(typeDeclaration)) {
try {
TypeBinding beanType = typeDeclaration.binding;
if (beanType != null) {
String id = getBeanId2(null, beanType, Flags.isStatic(typeDeclaration.modifiers));
Optional<Range> nameRange = Optional.of(new DocumentRegion(doc, typeDeclaration.sourceStart, typeDeclaration.sourceEnd).asRange());
if (nameRange.isPresent()) {
List<CodeLens> codeLenses = assembleCodeLenses2(project, processLiveData, liveData -> definedBean(liveData, getBeanType2(beanType), id), doc,
nameRange.get(), typeDeclaration);
if (codeLenses != null) {
return codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(nameRange.get())) : codeLenses;
}
}
}
} catch (Exception e) {
LOG.error("", e);
}
}
return ImmutableList.of();
}
@Override
protected List<LiveBean> findWiredBeans(IJavaProject project, SpringProcessLiveData liveData, List<LiveBean> relevantBeans,
@@ -178,6 +225,15 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
return typeDeclaration == null ? Collections.emptyList() : LiveHoverUtils.findAllDependencyBeans(liveData, relevantBeans);
}
private boolean isComponentAnnotatedType2(org.eclipse.jdt.internal.compiler.ast.TypeDeclaration typeDeclaration) {
for (org.eclipse.jdt.internal.compiler.ast.Annotation a : typeDeclaration.annotations) {
if (a.type != null && a.type.resolvedType != null && isComponentAnnotation2(a.type.resolvedType)) {
return true;
}
}
return false;
}
private boolean isComponentAnnotatedType(TypeDeclaration typeDeclaration) {
List<?> modifiers = typeDeclaration.modifiers();
for (Object modifier : modifiers) {
@@ -201,5 +257,17 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
return false;
}
private boolean isComponentAnnotation2(TypeBinding type) {
Set<String> transitiveSuperAnnotations = AnnotationHierarchies.getTransitiveSuperAnnotations2(type);
for (String annotationType : transitiveSuperAnnotations) {
if (Annotations.COMPONENT.equals(annotationType)) {
return true;
}
}
return false;
}
}

View File

@@ -2,7 +2,11 @@ package org.springframework.ide.vscode.boot.java.utils;
import java.util.Arrays;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
public class CuDeclarationUtils {
@@ -20,5 +24,23 @@ public class CuDeclarationUtils {
// line start from 1 hence -1 and -1 for getting end of previous line
return line == 0 ? offset : offset - cu.compilationResult.lineSeparatorPositions[line - 2] - 1; // -1 at end because offset is for the line separator on the previous line
}
public static String getQualifiedName(TypeBinding type) {
StringBuilder sb = new StringBuilder();
sb.append(type.qualifiedPackageName());
sb.append('.');
sb.append(type.qualifiedSourceName());
return sb.toString();
}
public static MethodDeclaration getAnnotatedMethod(Annotation annotation) {
// ASTNode parent = annotation.recipient.;
// if (parent instanceof MethodDeclaration) {
// return (MethodDeclaration)parent;
// }
return null;
}
}

View File

@@ -10,6 +10,7 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.utils.test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -29,17 +30,24 @@ import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.core.INameEnvironmentWithProgress;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
import org.springframework.ide.vscode.boot.java.utils.CuDeclarationUtils;
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
@@ -159,4 +167,80 @@ public class AstParserTest {
}, new CompilationUnitScope(cu, new CompilerOptions(CompilationUnitCache.createCompilerOptions())/*new LookupEnvironment(null, new CompilerOptions(CompilationUnitCache.createCompilerOptions()), null, envTuple.getT2())*/));
}
@Test
void testCuDeclarationAnnotations() throws Exception {
URL sourceUrl = SourceLinks.source(jp, "org.springframework.boot.autoconfigure.SpringBootApplication").get();
URI uri = sourceUrl.toURI();
String unitName = "SpringBootApplication";
char[] content = IOUtils.toString(uri).toCharArray();
Tuple2<List<Classpath>, INameEnvironmentWithProgress> envTuple = CompilationUnitCache.createLookupEnvTuple(jp);
CompilationUnitDeclaration cu = CompilationUnitCache.parse3(content, uri.toASCIIString(), unitName, envTuple.getT1(), envTuple.getT2());
assertNotNull(cu);
cu.traverse(new org.eclipse.jdt.internal.compiler.ASTVisitor() {
@Override
public boolean visit(org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation annotation, BlockScope scope) {
processAnnotation(annotation);
return super.visit(annotation, scope);
}
@Override
public boolean visit(org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation annotation, ClassScope scope) {
processAnnotation(annotation);
return super.visit(annotation, scope);
}
@Override
public boolean visit(org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation annotation,
BlockScope scope) {
processAnnotation(annotation);
return super.visit(annotation, scope);
}
@Override
public boolean visit(org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation annotation,
ClassScope scope) {
processAnnotation(annotation);
return super.visit(annotation, scope);
}
@Override
public boolean visit(org.eclipse.jdt.internal.compiler.ast.NormalAnnotation annotation, BlockScope scope) {
processAnnotation(annotation);
return super.visit(annotation, scope);
}
@Override
public boolean visit(org.eclipse.jdt.internal.compiler.ast.NormalAnnotation annotation, ClassScope scope) {
processAnnotation(annotation);
return super.visit(annotation, scope);
}
private void processAnnotation(Annotation a) {
if (a.recipient == null) {
System.out.println(CuDeclarationUtils.getQualifiedName(a.resolvedType));
} else {
if (a.recipient instanceof MethodBinding) {
MethodBinding mb = (MethodBinding) a.recipient;
assertEquals("org.springframework.boot.autoconfigure.SpringBootApplication", CuDeclarationUtils.getQualifiedName(mb.declaringClass));
} else if (a.recipient instanceof TypeBinding) {
assertEquals("org.springframework.boot.autoconfigure.SpringBootApplication", CuDeclarationUtils.getQualifiedName((TypeBinding)a.recipient));
} else if (a.recipient instanceof FieldBinding) {
FieldBinding fb = (FieldBinding) a.recipient;
assertEquals("org.springframework.boot.autoconfigure.SpringBootApplication", CuDeclarationUtils.getQualifiedName(fb.declaringClass));
}
}
}
}, new CompilationUnitScope(cu, new CompilerOptions(CompilationUnitCache.createCompilerOptions())/*new LookupEnvironment(null, new CompilerOptions(CompilationUnitCache.createCompilerOptions()), null, envTuple.getT2())*/));
}
}