diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTParserCleanupEnabled.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTParserCleanupEnabled.java new file mode 100644 index 000000000..8760dac4a --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ASTParserCleanupEnabled.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2024 Broadcom + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Broadcom - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.utils; + +import java.util.Map; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ASTParser; +import org.eclipse.jdt.core.dom.FileASTRequestor; + +/** + * Wrapper around a JDT AST parser to enable cleanup functionality on the lookup environment, + * which is otherwise hidden and not accessible from the outside + */ +public class ASTParserCleanupEnabled { + + private final ASTParser parser; +// private final INameEnvironmentWithProgress environment; + private final Map options; +// private final int flags; +// private final int apiLevel; + + public ASTParserCleanupEnabled(String[] classpathEntries, String[] sourceEntries, boolean ignoreMethodBodies) { + parser = ASTParser.newParser(AST.JLS21); + options = JavaCore.getOptions(); + JavaCore.setComplianceOptions(JavaCore.VERSION_21, options); + + parser.setCompilerOptions(options); + parser.setKind(ASTParser.K_COMPILATION_UNIT); + parser.setStatementsRecovery(true); + parser.setBindingsRecovery(true); + parser.setResolveBindings(true); + parser.setIgnoreMethodBodies(ignoreMethodBodies); + + parser.setEnvironment(classpathEntries, sourceEntries, null, false); + +// List classpaths = CUResolver.getClasspath(parser); +// environment = CUResolver.createLookupEnvironment(classpaths.toArray(new Classpath[classpaths.size()])); +// +// apiLevel = AST.JLS21; +// +// int flags = 0; +// flags |= ICompilationUnit.ENABLE_STATEMENTS_RECOVERY; +// flags |= ICompilationUnit.ENABLE_BINDINGS_RECOVERY; +// +// if (ignoreMethodBodies) { +// flags |= ICompilationUnit.IGNORE_METHOD_BODIES; +// } +// +// this.flags = flags; + + } + + public void setUnitName(String unitName) { + this.parser.setUnitName(unitName); + } + + public void setSource(char[] source) { + this.parser.setSource(source); + } + + public ASTNode createAST(IProgressMonitor monitor) { + return this.parser.createAST(monitor); + } + + public void createASTs(String[] sourceFilePaths, String[] encodings, String[] bindingKeys, + FileASTRequestor requestor, IProgressMonitor monitor) { + +// CUResolver.resolve(sourceFilePaths, encodings, bindingKeys, requestor, apiLevel, options, flags, environment); + this.parser.createASTs(sourceFilePaths, encodings, bindingKeys, requestor, monitor); + } + + public void cleanup() { +// environment.cleanup(); + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CUResolver.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CUResolver.java index 395501302..dd8d9455a 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CUResolver.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CUResolver.java @@ -24,6 +24,7 @@ import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.WorkingCopyOwner; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.FileASTRequestor; import org.eclipse.jdt.internal.compiler.ICompilerRequestor; import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy; import org.eclipse.jdt.internal.compiler.IProblemFactory; @@ -217,6 +218,27 @@ class CUResolver { return null; }; + private static final Supplier RESOLVE_SOURCE_FILES_METHOD = () -> { + try { + Class clazz = COMPILATION_UNIT_RESOLVER_CLASS.get(); + if (clazz != null) { + Method resolveMethod = clazz.getDeclaredMethod("resolve", + String[].class, // sourceCompilationUnits + String[].class, // encodings + String[].class, //bindingKeys + FileASTRequestor.class, // astRequestor + int.class, // apiLevel + Map.class, // compilerOptions + int.class); // flags + resolveMethod.setAccessible(true); + return resolveMethod; + } + } catch (NoSuchMethodException | SecurityException e) { + log.error("{}", e); + } + return null; + }; + private static final Supplier CONVERT_METHOD = () -> { try { Class clazz = COMPILATION_UNIT_RESOLVER_CLASS.get(); @@ -300,6 +322,57 @@ class CUResolver { return null; } + static void resolve( + String[] sourceUnits, + String[] encodings, + String[] bindingKeys, + FileASTRequestor requestor, + int apiLevel, + Map options, + int flags, + INameEnvironmentWithProgress environment) { + + CancelableProblemFactory problemFactory = null; + try { + problemFactory = new CancelableProblemFactory(new NullProgressMonitor()); + + CompilerOptions compilerOptions = (CompilerOptions) GET_COMPILER_OPTIONS_METHOD.get().invoke(null, options, + (flags & ICompilationUnit.ENABLE_STATEMENTS_RECOVERY) != 0); + compilerOptions.ignoreMethodBodies = (flags & ICompilationUnit.IGNORE_METHOD_BODIES) != 0; + + Object resolver = COMPILATION_UNIT_RESOLVER_CONSTRUCTOR.get().newInstance(environment, + GET_HANDLER_POLICY_METHOD.get().invoke(null), compilerOptions, + GET_REQUESTOR_METHOD.get().invoke(null), problemFactory, new NullProgressMonitor(), false); + + // CompilationUnitResolver resolver = + // new CompilationUnitResolver( + // environment, + // getHandlingPolicy(), + // compilerOptions, + // getRequestor(), + // problemFactory, + // subMonitor, + // false); + + RESOLVE_SOURCE_FILES_METHOD.get().invoke(resolver, + sourceUnits, encodings, bindingKeys, requestor, apiLevel, options, flags); + + // resolver.resolve(sourceUnits, encodings, bindingKeys, requestor, apiLevel, options, flags); + } + catch (Exception e) { + log.error("{}", e); + } + finally { + if (environment != null) { + environment.setMonitor(null); // don't hold a reference to this external object + } + if (problemFactory != null) { + problemFactory.monitor = null; // don't hold a reference to this external object + } + } + } + + static CompilationUnitDeclaration parse(org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit, Map options, int flags) { try { return (CompilationUnitDeclaration) PARSE_METHOD.get() diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java index 2a316cfec..9914ab2b0 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java @@ -34,9 +34,6 @@ import java.util.stream.Stream; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.tuple.Pair; -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.ASTVisitor; import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.CompilationUnit; @@ -264,7 +261,7 @@ public class SpringIndexerJava implements SpringIndexer { private void scanFile(IJavaProject project, DocumentDescriptor updatedDoc, String content) throws Exception { final boolean ignoreMethodBodies = false; - ASTParser parser = createParser(project, ignoreMethodBodies); + ASTParserCleanupEnabled parser = createParser(project, ignoreMethodBodies); String docURI = updatedDoc.getDocURI(); long lastModified = updatedDoc.getLastModified(); @@ -420,8 +417,9 @@ public class SpringIndexerJava implements SpringIndexer { List chunks = createChunks(javaFiles, this.scanChunkSize); for(int i = 0; i < chunks.size(); i++) { - ASTParser parser = createParser(project, ignoreMethodBodies); + ASTParserCleanupEnabled parser = createParser(project, ignoreMethodBodies); parser.createASTs(chunks.get(i), null, new String[0], requestor, null); + parser.cleanup(); } EnhancedSymbolInformation[] symbols = generatedSymbols.stream().map(cachedSymbol -> cachedSymbol.getEnhancedSymbol()).toArray(EnhancedSymbolInformation[]::new); @@ -572,8 +570,9 @@ public class SpringIndexerJava implements SpringIndexer { } }; - ASTParser parser = createParser(project, ignoreMethodBodies); + ASTParserCleanupEnabled parser = createParser(project, ignoreMethodBodies); parser.createASTs(javaFiles, null, new String[0], requestor, null); + parser.cleanup(); return (String[]) nextPassFiles.toArray(new String[nextPassFiles.size()]); } @@ -768,22 +767,11 @@ public class SpringIndexerJava implements SpringIndexer { return null; } - public static ASTParser createParser(IJavaProject project, boolean ignoreMethodBodies) throws Exception { + public static ASTParserCleanupEnabled createParser(IJavaProject project, boolean ignoreMethodBodies) throws Exception { String[] classpathEntries = getClasspathEntries(project); String[] sourceEntries = getSourceEntries(project); - ASTParser parser = ASTParser.newParser(AST.JLS21); - Map options = JavaCore.getOptions(); - JavaCore.setComplianceOptions(JavaCore.VERSION_21, options); - parser.setCompilerOptions(options); - parser.setKind(ASTParser.K_COMPILATION_UNIT); - parser.setStatementsRecovery(true); - parser.setBindingsRecovery(true); - parser.setResolveBindings(true); - parser.setIgnoreMethodBodies(ignoreMethodBodies); - - parser.setEnvironment(classpathEntries, sourceEntries, null, false); - return parser; + return new ASTParserCleanupEnabled(classpathEntries, sourceEntries, ignoreMethodBodies); } private static String[] getClasspathEntries(IJavaProject project) throws Exception {