From e8ee9a7f6e854ba5bf4aab2a2cd1b2742786bce1 Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Wed, 5 Jun 2019 10:36:20 -0400 Subject: [PATCH] Alternative AST parsing --- .../boot/java/utils/CompilationUnitCache.java | 108 ++++++++++++++++++ .../ide/vscode/boot/test/SomeTest.java | 62 ++++++++++ 2 files changed, 170 insertions(+) create mode 100644 headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/SomeTest.java 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 ff4dc4b94..3659147de 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 @@ -11,8 +11,11 @@ package org.springframework.ide.vscode.boot.java.utils; import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; import java.net.URI; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -23,10 +26,24 @@ import java.util.function.Function; import java.util.stream.Stream; import org.apache.commons.io.IOUtils; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.ITypeRoot; 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.ICompilerRequestor; +import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy; +import org.eclipse.jdt.internal.compiler.IProblemFactory; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath; +import org.eclipse.jdt.internal.compiler.env.INameEnvironment; +import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.eclipse.jdt.internal.core.BasicCompilationUnit; +import org.eclipse.jdt.internal.core.CancelableProblemFactory; +import org.eclipse.jdt.internal.core.INameEnvironmentWithProgress; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.java.IClasspath; @@ -184,7 +201,98 @@ public final class CompilationUnitCache implements DocumentContentProvider { return cu; } + + @SuppressWarnings("unchecked") + public static CompilationUnit parse2(char[] source, String docURI, String unitName, String[] classpathEntries) throws Exception { + ASTParser parser = ASTParser.newParser(AST.JLS11); + Map options = JavaCore.getOptions(); + JavaCore.setComplianceOptions(JavaCore.VERSION_11, options); + parser.setCompilerOptions(options); + parser.setKind(ASTParser.K_COMPILATION_UNIT); + parser.setStatementsRecovery(true); + parser.setBindingsRecovery(true); + parser.setResolveBindings(false); + String[] sourceEntries = new String[] {}; + parser.setEnvironment(classpathEntries, sourceEntries, null, false); + + parser.setUnitName(unitName); + parser.setSource(source); + + Method getClasspathMethod = ASTParser.class.getDeclaredMethod("getClasspath"); + getClasspathMethod.setAccessible(true); + List classpaths = (List) getClasspathMethod.invoke(parser); + + Class clazz = Class.forName("org.eclipse.jdt.core.dom.CompilationUnitResolver"); + Constructor ctor = clazz.getConstructor( + INameEnvironment.class, + IErrorHandlingPolicy.class, + CompilerOptions.class, + ICompilerRequestor.class, + IProblemFactory.class, + IProgressMonitor.class, + boolean.class + ); + ctor.setAccessible(true); + + Classpath[] allEntries = new Classpath[classpaths.size()]; + classpaths.toArray(allEntries); + Class nameEnvironmentWithProgressClass = Class.forName("org.eclipse.jdt.core.dom.NameEnvironmentWithProgress"); + Constructor lookupCtor = nameEnvironmentWithProgressClass.getConstructor( + Classpath[].class, + String[].class, + IProgressMonitor.class + ); + lookupCtor.setAccessible(true); + INameEnvironmentWithProgress environment = (INameEnvironmentWithProgress) lookupCtor.newInstance(allEntries, null, new NullProgressMonitor()); + + Method handlerPolicyMethod = clazz.getDeclaredMethod("getHandlingPolicy"); + handlerPolicyMethod.setAccessible(true); + + Method getRequestorMethod = clazz.getDeclaredMethod("getRequestor"); + getRequestorMethod.setAccessible(true); + + CancelableProblemFactory problemFactory = new CancelableProblemFactory(new NullProgressMonitor()); + + Method compilerOptionsMethod = clazz.getDeclaredMethod("getCompilerOptions", Map.class, boolean.class); + compilerOptionsMethod.setAccessible(true); + Object compilerOptionsObj = compilerOptionsMethod.invoke(parser, options, true); + + Object resolver = ctor.newInstance( + environment, + handlerPolicyMethod.invoke(null), + compilerOptionsObj, + getRequestorMethod.invoke(null), + problemFactory, + new NullProgressMonitor(), + false + ); + + BasicCompilationUnit sourceUnit = new BasicCompilationUnit(source, null, unitName, (IJavaElement) null); + CompilationUnit cu = (CompilationUnit) parser.createAST(null); + + Class nodeSearcherClass = Class.forName("org.eclipse.jdt.core.dom.NodeSearcher"); + Method resolveMethod = clazz.getDeclaredMethod("resolve", + CompilationUnitDeclaration.class, + org.eclipse.jdt.internal.compiler.env.ICompilationUnit.class, + nodeSearcherClass, + boolean.class, + boolean.class, + boolean.class); + resolveMethod.setAccessible(true); + + CompilationUnitDeclaration unit = + (CompilationUnitDeclaration) resolveMethod.invoke(resolver, + null, // no existing compilation unit declaration + sourceUnit, + null, + true, // method verification + true, // analyze code + true); // generate code + + return cu; + } + private static String[] getClasspathEntries(IJavaProject project) throws Exception { if (project == null) { return new String[0]; diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/SomeTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/SomeTest.java new file mode 100644 index 000000000..314f0a108 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/SomeTest.java @@ -0,0 +1,62 @@ +package org.springframework.ide.vscode.boot.test; + +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.net.URI; +import java.net.URL; +import java.util.stream.Stream; + +import org.apache.commons.io.IOUtils; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.junit.Before; +import org.junit.Test; +import org.springframework.ide.vscode.boot.java.links.SourceLinks; +import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; +import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IClasspathUtil; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; +import org.springframework.ide.vscode.project.harness.ProjectsHarness; + + +public class SomeTest { + + private ProjectsHarness projects = ProjectsHarness.INSTANCE; + + private MavenJavaProject jp; + + @Before + public void setup() throws Exception { + jp = projects.mavenProject("empty-boot-15-web-app"); + 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, getClasspathEntries(jp)); + } + + private static String[] getClasspathEntries(IJavaProject project) throws Exception { + if (project == null) { + return new String[0]; + } else { + IClasspath classpath = project.getClasspath(); + Stream classpathEntries = IClasspathUtil.getAllBinaryRoots(classpath).stream(); + return classpathEntries + .filter(file -> file.exists()) + .map(file -> file.getAbsolutePath()).toArray(String[]::new); + } + } + + +}