Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,16 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,

return launch(launchArguments, response, context).thenCompose(res -> {
if (res.success) {
ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class);

Map<String, Object> 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);
Expand Down Expand Up @@ -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 <code>Runtime.getRuntime().exec(cmdArray)</code>.
*
* @param args command line as a single string.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,20 @@

package com.microsoft.java.debug.plugin.internal.eval;

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;

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;
Expand All @@ -29,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;
Expand All @@ -41,6 +49,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;

Expand All @@ -55,6 +64,10 @@ public class JdtEvaluationProvider implements IEvaluationProvider {

private IDebugAdapterContext context;

private List<IJavaProject> possibleProjects;

private Set<String> visitedClassNames = new HashSet<>();

public JdtEvaluationProvider() {
}

Expand All @@ -70,15 +83,11 @@ public void initialize(IDebugAdapterContext context, Map<String, Object> props)
@Override
public CompletableFuture<Value> evaluate(String expression, ThreadReference thread, int depth) {
CompletableFuture<Value> completableFuture = new CompletableFuture<>();
String projectName = (String) options.get(Constants.PROJECTNAME);

if (debugTarget == null) {
String projectName = (String) options.get(Constants.PROJECTNAME);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please put the logic inside the initialize() of this provider. We only need to look for the project once per session.
Trying to calculate project everytime users evaluate expressions is overkill.

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);
initializeJavaProject(projectName, thread, depth);
}

if (project == null) {
Expand All @@ -98,15 +107,16 @@ 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.");
completableFuture.completeExceptionally(
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);
Expand All @@ -132,6 +142,91 @@ protected synchronized void initialize() {
return completableFuture;
}

/**
* 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<IJavaProject> projects = Arrays.stream(root.getProjects()).map(JdtUtils::getJavaProject).filter(p -> {
try {
return p != null && p.hasBuildState();
} catch (Exception e) {
// ignore
}
return false;
}).collect(Collectors.toList());


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
}
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<IJavaProject> 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.");
}

} catch (Exception ex) {
// ignore
}

logger.severe("Cannot evaluate when project is not specified.");
throw new IllegalStateException("Please specify projectName in launch.json.");
}


private JDIStackFrame createStackFrame(JDIThread thread, int depth) {
try {
IStackFrame[] jdiStackFrames = thread.getStackFrames();
Expand Down