From 89b1140310671c6aea342eaa6e638ec756443b43 Mon Sep 17 00:00:00 2001 From: andxu Date: Mon, 15 Jan 2018 12:14:04 +0800 Subject: [PATCH 1/2] Provide the failover way when projectName is not specifed on evaluation. --- .../internal/eval/JdtEvaluationProvider.java | 50 ++++++++++++++++--- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java index e1879fd6d..2cc6382a4 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java @@ -11,13 +11,18 @@ package com.microsoft.java.debug.plugin.internal.eval; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.logging.Logger; +import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.reflect.FieldUtils; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.ILaunch; @@ -41,6 +46,7 @@ import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; import com.microsoft.java.debug.core.adapter.IEvaluationProvider; import com.microsoft.java.debug.plugin.internal.JdtUtils; +import com.sun.jdi.StackFrame; import com.sun.jdi.ThreadReference; import com.sun.jdi.Value; @@ -70,15 +76,11 @@ public void initialize(IDebugAdapterContext context, Map props) @Override public CompletableFuture evaluate(String expression, ThreadReference thread, int depth) { CompletableFuture completableFuture = new CompletableFuture<>(); - String projectName = (String) options.get(Constants.PROJECTNAME); + if (debugTarget == null) { + String projectName = (String) options.get(Constants.PROJECTNAME); if (project == null) { - if (StringUtils.isBlank(projectName)) { - logger.severe("Cannot evaluate when project is not specified."); - completableFuture.completeExceptionally(new IllegalStateException("Please specify projectName in launch.json.")); - return completableFuture; - } - project = JdtUtils.getJavaProject(projectName); + project = calculateJavaProject(projectName, thread, depth); } if (project == null) { @@ -98,8 +100,8 @@ protected synchronized void initialize() { } }; } - JDIThread jdiThread = getMockJDIThread(thread); + JDIStackFrame stackframe = createStackFrame(jdiThread, depth); if (stackframe == null) { logger.severe("Cannot evaluate because the stackframe is not available."); @@ -107,6 +109,7 @@ protected synchronized void initialize() { new IllegalStateException("Cannot evaluate because the stackframe is not available.")); return completableFuture; } + try { ASTEvaluationEngine engine = new ASTEvaluationEngine(project, debugTarget); ICompiledExpression ie = engine.getCompiledExpression(expression, stackframe); @@ -132,6 +135,37 @@ protected synchronized void initialize() { return completableFuture; } + private IJavaProject calculateJavaProject(String projectName, ThreadReference thread, int depth) { + if (StringUtils.isBlank(projectName)) { + try { + StackFrame sf = thread.frame(depth); + String typeName = sf.location().method().declaringType().name(); + + // check whether the project is the only java project in workspace + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + List projects = Arrays.stream(root.getProjects()).map(JdtUtils::getJavaProject).filter(p -> { + try { + return p.findType(typeName) != null && p.hasBuildState(); + } catch (Exception e) { + // ignore + } + return false; + }).collect(Collectors.toList()); + + if (projects.size() == 1) { + return projects.get(0); + } + } catch (Exception ex) { + // ignore + } + + logger.severe("Cannot evaluate when project is not specified."); + throw new IllegalStateException("Please specify projectName in launch.json."); + } + return JdtUtils.getJavaProject(projectName); + } + + private JDIStackFrame createStackFrame(JDIThread thread, int depth) { try { IStackFrame[] jdiStackFrames = thread.getStackFrames(); From f927e52eb0bb39c66e83de14512ff6b4b5450ece Mon Sep 17 00:00:00 2001 From: andxu Date: Tue, 16 Jan 2018 10:37:24 +0800 Subject: [PATCH 2/2] Provide the failover way when projectName is not specifed on evaluation. --- .../java/debug/core/adapter/Constants.java | 1 + .../adapter/handler/LaunchRequestHandler.java | 8 +- .../internal/eval/JdtEvaluationProvider.java | 105 ++++++++++++++---- 3 files changed, 90 insertions(+), 24 deletions(-) diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/Constants.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/Constants.java index 760241517..d885967cf 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/Constants.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/Constants.java @@ -14,4 +14,5 @@ public final class Constants { public static final String PROJECTNAME = "projectName"; public static final String DEBUGGEE_ENCODING = "debuggeeEncoding"; + public static final String MAINCLASS = "mainClass"; } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java index a4c1d27c7..92352ed31 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java @@ -108,12 +108,16 @@ public CompletableFuture handle(Command command, Arguments arguments, return launch(launchArguments, response, context).thenCompose(res -> { if (res.success) { - ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class); + Map options = new HashMap<>(); options.put(Constants.DEBUGGEE_ENCODING, context.getDebuggeeEncoding()); if (launchArguments.projectName != null) { options.put(Constants.PROJECTNAME, launchArguments.projectName); } + if (launchArguments.mainClass != null) { + options.put(Constants.MAINCLASS, launchArguments.mainClass); + } + ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class); sourceProvider.initialize(context, options); IEvaluationProvider evaluationProvider = context.getProvider(IEvaluationProvider.class); evaluationProvider.initialize(context, options); @@ -336,7 +340,7 @@ private String[] constructLaunchCommands(LaunchArguments launchArguments, boolea } /** - * Parses the given command line into separate arguments that can be passed + * Parses the given command line i`nto separate arguments that can be passed * to Runtime.getRuntime().exec(cmdArray). * * @param args command line as a single string. diff --git a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java index 2cc6382a4..9adf6b457 100644 --- a/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java +++ b/com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java @@ -13,8 +13,10 @@ import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -34,6 +36,7 @@ import org.eclipse.debug.core.sourcelookup.AbstractSourceLookupDirector; import org.eclipse.debug.core.sourcelookup.containers.ProjectSourceContainer; import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.debug.eval.ICompiledExpression; import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget; import org.eclipse.jdt.internal.debug.core.model.JDIStackFrame; @@ -61,6 +64,10 @@ public class JdtEvaluationProvider implements IEvaluationProvider { private IDebugAdapterContext context; + private List possibleProjects; + + private Set visitedClassNames = new HashSet<>(); + public JdtEvaluationProvider() { } @@ -80,7 +87,7 @@ public CompletableFuture evaluate(String expression, ThreadReference thre if (debugTarget == null) { String projectName = (String) options.get(Constants.PROJECTNAME); if (project == null) { - project = calculateJavaProject(projectName, thread, depth); + initializeJavaProject(projectName, thread, depth); } if (project == null) { @@ -135,34 +142,88 @@ protected synchronized void initialize() { return completableFuture; } - private IJavaProject calculateJavaProject(String projectName, ThreadReference thread, int depth) { - if (StringUtils.isBlank(projectName)) { + /** + * Prepare a list of possible java projects in workspace which contains the main class. + * @param projectName the possible project name specified by launch.json + * @param mainclass the main class specified by launch.json for finding possible projects + */ + private void initializePossibleProjects(String projectName, String mainclass) { + + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + List projects = Arrays.stream(root.getProjects()).map(JdtUtils::getJavaProject).filter(p -> { try { - StackFrame sf = thread.frame(depth); - String typeName = sf.location().method().declaringType().name(); + return p != null && p.hasBuildState(); + } catch (Exception e) { + // ignore + } + return false; + }).collect(Collectors.toList()); - // check whether the project is the only java project in workspace - IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); - List projects = Arrays.stream(root.getProjects()).map(JdtUtils::getJavaProject).filter(p -> { - try { - return p.findType(typeName) != null && p.hasBuildState(); - } catch (Exception e) { - // ignore - } - return false; - }).collect(Collectors.toList()); - if (projects.size() == 1) { - return projects.get(0); + if (projects.size() > 1 && StringUtils.isNotBlank(mainclass)) { + projects = Arrays.stream(root.getProjects()).map(JdtUtils::getJavaProject).filter(p -> { + try { + return p.findType(mainclass) != null; + } catch (JavaModelException e) { + // ignore } - } catch (Exception ex) { - // ignore + return false; + }).collect(Collectors.toList()); + visitedClassNames.add(mainclass); + } + + if (projects.size() == 1) { + project = projects.get(0); + } + + possibleProjects = projects; + } + + private void initializeJavaProject(String projectName, ThreadReference thread, int depth) { + if (possibleProjects == null) { + initializePossibleProjects((String) options.get(Constants.PROJECTNAME), (String) options.get(Constants.MAINCLASS)); + if (project != null) { + return; + } + } + + if (possibleProjects.size() == 0) { + logger.severe("No project is available for evaluation."); + throw new IllegalStateException("No project is available for evaluation."); + } + + try { + StackFrame sf = thread.frame(depth); + String typeName = sf.location().method().declaringType().name(); + // check whether the project is the only java project in workspace + List validProjects = visitedClassNames.contains(typeName) ? possibleProjects + : possibleProjects.stream().filter(p -> { + try { + return !visitedClassNames.contains(typeName) && p.findType(typeName) != null; + } catch (Exception e) { + // ignore + } + return false; + }).collect(Collectors.toList()); + visitedClassNames.add(typeName); + if (validProjects.size() == 1) { + project = validProjects.get(0); + } else if (validProjects.size() == 0) { + logger.severe("No project is available for evaluation."); + throw new IllegalStateException("No project is available for evaluation, ."); + } else { + // narrow down projects + possibleProjects = validProjects; + logger.severe("Multiple projects are valid for evaluation."); + throw new IllegalStateException("Multiple projects are found, please specify projectName in launch.json."); } - logger.severe("Cannot evaluate when project is not specified."); - throw new IllegalStateException("Please specify projectName in launch.json."); + } catch (Exception ex) { + // ignore } - return JdtUtils.getJavaProject(projectName); + + logger.severe("Cannot evaluate when project is not specified."); + throw new IllegalStateException("Please specify projectName in launch.json."); }