diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 41c85cb62..1ace44224 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,7 +28,7 @@ jobs: ${{ runner.os }}-maven- - name: Verify - run: ./mvnw clean verify + run: ./mvnw clean verify -U - name: Checkstyle run: ./mvnw checkstyle:check @@ -85,7 +85,7 @@ jobs: ${{ runner.os }}-maven- - name: Verify - run: ./mvnw clean verify + run: ./mvnw clean verify -U - name: Checkstyle run: ./mvnw checkstyle:check diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java index 1ebf4c7c3..c96b98342 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java @@ -32,6 +32,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.commons.lang3.StringUtils; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; @@ -56,6 +57,7 @@ import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.LambdaExpression; +import org.eclipse.jdt.core.dom.UnnamedClass; import org.eclipse.jdt.core.manipulation.CoreASTProvider; import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; import org.eclipse.jdt.launching.IVMInstall; @@ -172,6 +174,11 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB .map(sourceBreakpoint -> new JavaBreakpointLocation(sourceBreakpoint.line, sourceBreakpoint.column)) .toArray(JavaBreakpointLocation[]::new); if (astUnit != null) { + List types = astUnit.types(); + String unnamedClass = null; + if (types.size() == 1 && types.get(0) instanceof UnnamedClass) { + unnamedClass = inferPrimaryTypeName(sourceUri, astUnit); + } Map resolvedLocations = new HashMap<>(); for (JavaBreakpointLocation sourceLocation : sourceLocations) { int sourceLine = sourceLocation.lineNumber(); @@ -222,7 +229,7 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB // be hit in current implementation. if (sourceLine == locator.getLineLocation() && locator.getLocationType() == BreakpointLocationLocator.LOCATION_LINE) { - sourceLocation.setClassName(locator.getFullyQualifiedTypeName()); + sourceLocation.setClassName(StringUtils.isBlank(unnamedClass) ? locator.getFullyQualifiedTypeName() : unnamedClass); if (resolvedLocations.containsKey(sourceLine)) { sourceLocation.setAvailableBreakpointLocations(resolvedLocations.get(sourceLine)); } else { @@ -231,7 +238,7 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB resolvedLocations.put(sourceLine, inlineLocations); } } else if (locator.getLocationType() == BreakpointLocationLocator.LOCATION_METHOD) { - sourceLocation.setClassName(locator.getFullyQualifiedTypeName()); + sourceLocation.setClassName(StringUtils.isBlank(unnamedClass) ? locator.getFullyQualifiedTypeName() : unnamedClass); sourceLocation.setMethodName(locator.getMethodName()); sourceLocation.setMethodSignature(locator.getMethodSignature()); } @@ -241,6 +248,27 @@ public JavaBreakpointLocation[] getBreakpointLocations(String sourceUri, SourceB return sourceLocations; } + private String inferPrimaryTypeName(String uri, CompilationUnit astUnit) { + String fileName = ""; + String filePath = AdapterUtils.toPath(uri); + if (filePath != null && Files.isRegularFile(Paths.get(filePath))) { + fileName = Paths.get(filePath).getFileName().toString(); + } else if (astUnit.getTypeRoot() != null) { + fileName = astUnit.getTypeRoot().getElementName(); + } + + if (StringUtils.isNotBlank(fileName)) { + String[] extensions = JavaCore.getJavaLikeExtensions(); + for (String extension : extensions) { + if (fileName.endsWith("." + extension)) { + return fileName.substring(0, fileName.length() - 1 - extension.length()); + } + } + } + + return fileName; + } + private BreakpointLocation[] getInlineBreakpointLocations(final CompilationUnit astUnit, int sourceLine) { List locations = new ArrayList<>(); // The starting position of each line is the default breakpoint location for diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java index f70d0c044..38ad1532b 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainClassHandler.java @@ -43,6 +43,7 @@ import org.eclipse.jdt.core.search.SearchParticipant; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.core.search.SearchRequestor; +import org.eclipse.jdt.internal.core.SourceMethod; import org.eclipse.jdt.ls.core.internal.ProjectUtils; import org.eclipse.jdt.ls.core.internal.ResourceUtils; import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; @@ -101,8 +102,7 @@ private List resolveMainClassUnderPaths(List parentPaths) // Limit to search main method from source code only. IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(ProjectUtils.getJavaProjects(), IJavaSearchScope.REFERENCED_PROJECTS | IJavaSearchScope.SOURCES); - SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, - IJavaSearchConstants.DECLARATIONS, SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_EXACT_MATCH); + SearchPattern pattern = createMainMethodSearchPattern(); final List res = new ArrayList<>(); SearchRequestor requestor = new SearchRequestor() { @Override @@ -110,40 +110,36 @@ public void acceptSearchMatch(SearchMatch match) { Object element = match.getElement(); if (element instanceof IMethod) { IMethod method = (IMethod) element; - try { - if (method.isMainMethod()) { - IResource resource = method.getResource(); - if (resource != null) { - IProject project = resource.getProject(); - if (project != null) { - String mainClass = method.getDeclaringType().getFullyQualifiedName(); - IJavaProject javaProject = JdtUtils.getJavaProject(project); - if (javaProject != null) { - String moduleName = JdtUtils.getModuleName(javaProject); - if (moduleName != null) { - mainClass = moduleName + "/" + mainClass; - } + if (isMainMethod(method)) { + IResource resource = method.getResource(); + if (resource != null) { + IProject project = resource.getProject(); + if (project != null) { + String mainClass = method.getDeclaringType().getFullyQualifiedName(); + IJavaProject javaProject = JdtUtils.getJavaProject(project); + if (javaProject != null) { + String moduleName = JdtUtils.getModuleName(javaProject); + if (moduleName != null) { + mainClass = moduleName + "/" + mainClass; } - String projectName = ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getName()) ? null : project.getName(); - if (parentPaths.isEmpty() - || ResourceUtils.isContainedIn(project.getLocation(), parentPaths) - || isContainedInInvisibleProject(project, parentPaths)) { - String filePath = null; - - if (match.getResource() instanceof IFile) { - try { - filePath = match.getResource().getLocation().toOSString(); - } catch (Exception ex) { - // ignore - } + } + String projectName = ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getName()) ? null : project.getName(); + if (parentPaths.isEmpty() + || ResourceUtils.isContainedIn(project.getLocation(), parentPaths) + || isContainedInInvisibleProject(project, parentPaths)) { + String filePath = null; + + if (match.getResource() instanceof IFile) { + try { + filePath = match.getResource().getLocation().toOSString(); + } catch (Exception ex) { + // ignore } - res.add(new ResolutionItem(mainClass, projectName, filePath)); } + res.add(new ResolutionItem(mainClass, projectName, filePath)); } } } - } catch (JavaModelException e) { - // ignore } } } @@ -166,8 +162,7 @@ private List resolveMainClassUnderProject(final String projectNa IJavaProject javaProject = ProjectUtils.getJavaProject(projectName); IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(javaProject == null ? new IJavaProject[0] : new IJavaProject[] {javaProject}, IJavaSearchScope.REFERENCED_PROJECTS | IJavaSearchScope.SOURCES); - SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, - IJavaSearchConstants.DECLARATIONS, SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_EXACT_MATCH); + SearchPattern pattern = createMainMethodSearchPattern(); final List res = new ArrayList<>(); SearchRequestor requestor = new SearchRequestor() { @Override @@ -175,35 +170,31 @@ public void acceptSearchMatch(SearchMatch match) { Object element = match.getElement(); if (element instanceof IMethod) { IMethod method = (IMethod) element; - try { - if (method.isMainMethod()) { - IResource resource = method.getResource(); - if (resource != null) { - IProject project = resource.getProject(); - if (project != null) { - String mainClass = method.getDeclaringType().getFullyQualifiedName(); - IJavaProject javaProject = JdtUtils.getJavaProject(project); - if (javaProject != null) { - String moduleName = JdtUtils.getModuleName(javaProject); - if (moduleName != null) { - mainClass = moduleName + "/" + mainClass; - } + if (isMainMethod(method)) { + IResource resource = method.getResource(); + if (resource != null) { + IProject project = resource.getProject(); + if (project != null) { + String mainClass = method.getDeclaringType().getFullyQualifiedName(); + IJavaProject javaProject = JdtUtils.getJavaProject(project); + if (javaProject != null) { + String moduleName = JdtUtils.getModuleName(javaProject); + if (moduleName != null) { + mainClass = moduleName + "/" + mainClass; } + } - String filePath = null; - if (match.getResource() instanceof IFile) { - try { - filePath = match.getResource().getLocation().toOSString(); - } catch (Exception ex) { - // ignore - } + String filePath = null; + if (match.getResource() instanceof IFile) { + try { + filePath = match.getResource().getLocation().toOSString(); + } catch (Exception ex) { + // ignore } - res.add(new ResolutionItem(mainClass, projectName, filePath)); } + res.add(new ResolutionItem(mainClass, projectName, filePath)); } } - } catch (JavaModelException e) { - // ignore } } } @@ -221,6 +212,29 @@ public void acceptSearchMatch(SearchMatch match) { return resolutions; } + private SearchPattern createMainMethodSearchPattern() { + SearchPattern pattern1 = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, + IJavaSearchConstants.DECLARATIONS, SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_EXACT_MATCH); + SearchPattern pattern2 = SearchPattern.createPattern("main() void", IJavaSearchConstants.METHOD, + IJavaSearchConstants.DECLARATIONS, SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_EXACT_MATCH); + return SearchPattern.createOrPattern(pattern1, pattern2); + } + + private boolean isMainMethod(IMethod method) { + try { + if (method instanceof SourceMethod + && ((SourceMethod) method).isMainMethodCandidate()) { + return true; + } + + return method.isMainMethod(); + } catch (JavaModelException e) { + // do nothing + } + + return false; + } + private boolean isContainedInInvisibleProject(IProject project, Collection rootPaths) { if (project == null) { return false; diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java index a3c61e814..fb8dfc33e 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/ResolveMainMethodHandler.java @@ -15,6 +15,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; @@ -30,7 +31,11 @@ import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.ISourceReference; import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.eclipse.jdt.internal.core.SourceMethod; import org.eclipse.jdt.ls.core.internal.JDTUtils; import org.eclipse.jdt.ls.core.internal.handlers.DocumentLifeCycleHandler; import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; @@ -104,16 +109,58 @@ private static List searchMainMethods(ICompilationUnit compilationUnit) * Returns the main method defined in the type. */ public static IMethod getMainMethod(IType type) throws JavaModelException { + boolean allowInstanceMethod = isInstanceMainMethodSupported(type); + List methods = new ArrayList<>(); for (IMethod method : type.getMethods()) { - // Have at most one main method in the member methods of the type. + if (method instanceof SourceMethod + && ((SourceMethod) method).isMainMethodCandidate()) { + methods.add(method); + } + if (method.isMainMethod()) { - return method; + methods.add(method); + } + + if (!allowInstanceMethod && !methods.isEmpty()) { + return methods.get(0); } } + if (!methods.isEmpty()) { + methods.sort((method1, method2) -> { + return getMainMethodPriority(method1) - getMainMethodPriority(method2); + }); + + return methods.get(0); + } + return null; } + private static boolean isInstanceMainMethodSupported(IType type) { + Map options = type.getJavaProject().getOptions(true); + return CompilerOptions.versionToJdkLevel(options.get(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM)) >= ClassFileConstants.JDK21; + } + + private static int getMainMethodPriority(IMethod method) { + int flags = 0; + try { + flags = method.getFlags(); + } catch (JavaModelException e) { + // do nothing + } + String[] params = method.getParameterTypes(); + if (Flags.isStatic(flags) && params.length == 1) { + return 1; + } else if (Flags.isStatic(flags)) { + return 2; + } else if (params.length == 1) { + return 3; + } + + return 4; + } + private static List getPotentialMainClassTypes(ICompilationUnit compilationUnit) throws JavaModelException { List result = new ArrayList<>(); IType[] topLevelTypes = compilationUnit.getTypes(); diff --git a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target index 200e532e3..a52901d01 100644 --- a/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target +++ b/com.microsoft.java.debug.target/com.microsoft.java.debug.tp.target @@ -14,13 +14,18 @@ - + + + + + + - + diff --git a/pom.xml b/pom.xml index b039fe1c1..2917f4092 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ Java Debug Server for Visual Studio Code UTF-8 - 4.0.5 + 4.0.6 ${basedir}