Skip to content

Commit 5c6c967

Browse files
authored
Merge remote-tracking branch 'origin' into andy_cond3 (#158)
1 parent 0b3da27 commit 5c6c967

7 files changed

Lines changed: 151 additions & 18 deletions

File tree

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/Constants.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
package com.microsoft.java.debug.core.adapter;
1313

1414
public final class Constants {
15-
public static final String PROJECTNAME = "projectName";
15+
public static final String PROJECT_NAME = "projectName";
1616
public static final String DEBUGGEE_ENCODING = "debuggeeEncoding";
17+
public static final String MAIN_CLASS = "mainClass";
1718
}

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/AttachRequestHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
8585
Map<String, Object> options = new HashMap<>();
8686
options.put(Constants.DEBUGGEE_ENCODING, context.getDebuggeeEncoding());
8787
if (attachArguments.projectName != null) {
88-
options.put(Constants.PROJECTNAME, attachArguments.projectName);
88+
options.put(Constants.PROJECT_NAME, attachArguments.projectName);
8989
}
9090
ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class);
9191
sourceProvider.initialize(context, options);

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/LaunchRequestHandler.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,16 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
108108

109109
return launch(launchArguments, response, context).thenCompose(res -> {
110110
if (res.success) {
111-
ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class);
111+
112112
Map<String, Object> options = new HashMap<>();
113113
options.put(Constants.DEBUGGEE_ENCODING, context.getDebuggeeEncoding());
114114
if (launchArguments.projectName != null) {
115-
options.put(Constants.PROJECTNAME, launchArguments.projectName);
115+
options.put(Constants.PROJECT_NAME, launchArguments.projectName);
116116
}
117+
if (launchArguments.mainClass != null) {
118+
options.put(Constants.MAIN_CLASS, launchArguments.mainClass);
119+
}
120+
ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class);
117121
sourceProvider.initialize(context, options);
118122
IEvaluationProvider evaluationProvider = context.getProvider(IEvaluationProvider.class);
119123
evaluationProvider.initialize(context, options);

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/SetBreakpointsRequestHandler.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ private void registerBreakpointHandler(IDebugAdapterContext context) {
176176
if (conditionalBP != null) {
177177
CompletableFuture.runAsync(() -> {
178178
engine.evaluateForBreakpoint(conditionalBP, bpThread, manager.getBreakpointExpressionMap()).whenComplete((value, ex) -> {
179-
// TODO, notify user when error is raised.
180179
boolean resume = false;
180+
boolean resultNotBoolean = false;
181181
if (value != null && ex == null) {
182182
if (value instanceof BooleanValue) {
183183
resume = !((BooleanValue) value).booleanValue();
@@ -186,6 +186,8 @@ private void registerBreakpointHandler(IDebugAdapterContext context) {
186186
// get boolean value from java.lang.Boolean object
187187
Field field = ((ReferenceType) ((ObjectReference) value).type()).fieldByName("value");
188188
resume = !((BooleanValue) ((ObjectReference) value).getValue(field)).booleanValue();
189+
} else {
190+
resultNotBoolean = true;
189191
}
190192
}
191193
if (resume) {
@@ -194,6 +196,16 @@ private void registerBreakpointHandler(IDebugAdapterContext context) {
194196
engine.clearState(bpThread);
195197
} else {
196198
context.getProtocolServer().sendEvent(new Events.StoppedEvent("breakpoint", bpThread.uniqueID()));
199+
if (ex != null) {
200+
context.getProtocolServer().sendEvent(new Events.UserNotificationEvent(
201+
Events.UserNotificationEvent.NotificationType.ERROR,
202+
String.format("Breakpoint condition '%s' error: %s", conditionalBP.getCondition(), ex.getMessage())));
203+
} else if (value == null || resultNotBoolean) {
204+
context.getProtocolServer().sendEvent(new Events.UserNotificationEvent(
205+
Events.UserNotificationEvent.NotificationType.WARNING,
206+
String.format("Result of breakpoint condition '%s' is not a boolean, please correct your expression.",
207+
conditionalBP.getCondition())));
208+
}
197209
}
198210
});
199211

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Events.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public StoppedEvent(String reason, long threadId) {
4343
super("stopped");
4444
this.reason = reason;
4545
this.threadId = threadId;
46-
this.allThreadsStopped = false;
46+
allThreadsStopped = false;
4747
}
4848

4949
/**
@@ -194,4 +194,22 @@ public HotCodeReplaceEvent(ChangeType changeType, String message) {
194194
this.message = message;
195195
}
196196
}
197+
198+
public static class UserNotificationEvent extends DebugEvent {
199+
public enum NotificationType {
200+
ERROR, WARNING, INFORMATION
201+
}
202+
203+
public NotificationType notificationType;
204+
public String message;
205+
206+
/**
207+
* Constructor.
208+
*/
209+
public UserNotificationEvent(NotificationType notifyType, String message) {
210+
super("usernotification");
211+
this.notificationType = notifyType;
212+
this.message = message;
213+
}
214+
}
197215
}

com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtSourceLookUpProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ public String getSourceFileURI(String fullyQualifiedName, String sourcePath) {
183183

184184
private synchronized ISourceContainer[] getSourceContainers() {
185185
if (sourceContainers == null) {
186-
sourceContainers = JdtUtils.getSourceContainers((String) options.get(Constants.PROJECTNAME));
186+
sourceContainers = JdtUtils.getSourceContainers((String) options.get(Constants.PROJECT_NAME));
187187
}
188188

189189
return sourceContainers;

com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/eval/JdtEvaluationProvider.java

Lines changed: 109 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,20 @@
1111

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

14+
import java.util.Arrays;
1415
import java.util.HashMap;
16+
import java.util.HashSet;
17+
import java.util.List;
1518
import java.util.Map;
19+
import java.util.Set;
1620
import java.util.concurrent.CompletableFuture;
1721
import java.util.logging.Logger;
22+
import java.util.stream.Collectors;
1823

1924
import org.apache.commons.lang3.StringUtils;
2025
import org.apache.commons.lang3.reflect.FieldUtils;
26+
import org.eclipse.core.resources.IWorkspaceRoot;
27+
import org.eclipse.core.resources.ResourcesPlugin;
2128
import org.eclipse.core.runtime.CoreException;
2229
import org.eclipse.debug.core.DebugException;
2330
import org.eclipse.debug.core.ILaunch;
@@ -29,6 +36,7 @@
2936
import org.eclipse.debug.core.sourcelookup.AbstractSourceLookupDirector;
3037
import org.eclipse.debug.core.sourcelookup.containers.ProjectSourceContainer;
3138
import org.eclipse.jdt.core.IJavaProject;
39+
import org.eclipse.jdt.core.JavaModelException;
3240
import org.eclipse.jdt.debug.core.IJavaStackFrame;
3341
import org.eclipse.jdt.debug.eval.ICompiledExpression;
3442
import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
@@ -43,6 +51,7 @@
4351
import com.microsoft.java.debug.core.adapter.IDebugAdapterContext;
4452
import com.microsoft.java.debug.core.adapter.IEvaluationProvider;
4553
import com.microsoft.java.debug.plugin.internal.JdtUtils;
54+
import com.sun.jdi.StackFrame;
4655
import com.sun.jdi.ThreadReference;
4756
import com.sun.jdi.Value;
4857
import com.sun.jdi.VirtualMachine;
@@ -56,6 +65,10 @@ public class JdtEvaluationProvider implements IEvaluationProvider {
5665
private HashMap<String, Object> options = new HashMap<>();
5766
private IDebugAdapterContext context;
5867

68+
private List<IJavaProject> projectCandidates;
69+
70+
private Set<String> visitedClassNames = new HashSet<>();
71+
5972
public JdtEvaluationProvider() {
6073
}
6174

@@ -80,7 +93,7 @@ public CompletableFuture<Value> evaluateForBreakpoint(IBreakpoint breakpoint, Th
8093

8194
CompletableFuture<Value> completableFuture = new CompletableFuture<>();
8295
try {
83-
ensureDebugTarget(thread.virtualMachine());
96+
ensureDebugTarget(thread.virtualMachine(), thread, 0);
8497
JDIThread jdiThread = getMockJDIThread(thread);
8598
JDIStackFrame stackframe = (JDIStackFrame) jdiThread.getTopStackFrame();
8699

@@ -101,7 +114,7 @@ public CompletableFuture<Value> evaluateForBreakpoint(IBreakpoint breakpoint, Th
101114
public CompletableFuture<Value> evaluate(String expression, ThreadReference thread, int depth) {
102115
CompletableFuture<Value> completableFuture = new CompletableFuture<>();
103116
try {
104-
ensureDebugTarget(thread.virtualMachine());
117+
ensureDebugTarget(thread.virtualMachine(), thread, depth);
105118
JDIThread jdiThread = getMockJDIThread(thread);
106119
JDIStackFrame stackframe = createStackFrame(jdiThread, depth);
107120
if (stackframe == null) {
@@ -119,6 +132,91 @@ public CompletableFuture<Value> evaluate(String expression, ThreadReference thre
119132
}
120133
}
121134

135+
/**
136+
* Prepare a list of java project candidates in workspace which contains the main class.
137+
*
138+
* @param mainclass the main class specified by launch.json for finding project candidates
139+
*/
140+
private void initializeProjectCandidates(String mainclass) {
141+
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
142+
List<IJavaProject> projects = Arrays.stream(root.getProjects()).map(JdtUtils::getJavaProject).filter(p -> {
143+
try {
144+
return p != null && p.hasBuildState();
145+
} catch (Exception e) {
146+
// ignore
147+
}
148+
return false;
149+
}).collect(Collectors.toList());
150+
151+
152+
if (projects.size() > 1 && StringUtils.isNotBlank(mainclass)) {
153+
projects = Arrays.stream(root.getProjects()).map(JdtUtils::getJavaProject).filter(p -> {
154+
try {
155+
return p.findType(mainclass) != null;
156+
} catch (JavaModelException e) {
157+
// ignore
158+
}
159+
return false;
160+
}).collect(Collectors.toList());
161+
visitedClassNames.add(mainclass);
162+
}
163+
164+
if (projects.size() == 1) {
165+
project = projects.get(0);
166+
}
167+
168+
projectCandidates = projects;
169+
}
170+
171+
private void findJavaProjectByStackFrame(ThreadReference thread, int depth) {
172+
if (projectCandidates == null) {
173+
// initial candidate projects by main class (projects contains this main class)
174+
initializeProjectCandidates((String) options.get(Constants.MAIN_CLASS));
175+
if (project != null) {
176+
return;
177+
}
178+
}
179+
180+
if (projectCandidates.size() == 0) {
181+
logger.severe("No project is available for evaluation.");
182+
throw new IllegalStateException("No project is available for evaluation.");
183+
}
184+
185+
try {
186+
StackFrame sf = thread.frame(depth);
187+
String typeName = sf.location().method().declaringType().name();
188+
// narrow down candidate projects by current class
189+
List<IJavaProject> validProjects = visitedClassNames.contains(typeName) ? projectCandidates
190+
: projectCandidates.stream().filter(p -> {
191+
try {
192+
return !visitedClassNames.contains(typeName) && p.findType(typeName) != null;
193+
} catch (Exception e) {
194+
// ignore
195+
}
196+
return false;
197+
}).collect(Collectors.toList());
198+
visitedClassNames.add(typeName);
199+
if (validProjects.size() == 1) {
200+
project = validProjects.get(0);
201+
} else if (validProjects.size() == 0) {
202+
logger.severe("No project is available for evaluation.");
203+
throw new IllegalStateException("No project is available for evaluation, .");
204+
} else {
205+
// narrow down projects
206+
projectCandidates = validProjects;
207+
logger.severe("Multiple projects are valid for evaluation.");
208+
throw new IllegalStateException("Multiple projects are found, please specify projectName in launch.json.");
209+
}
210+
211+
} catch (Exception ex) {
212+
// ignore
213+
}
214+
215+
logger.severe("Cannot evaluate when the project is not specified.");
216+
throw new IllegalStateException("Please specify projectName in launch.json.");
217+
}
218+
219+
122220
private JDIStackFrame createStackFrame(JDIThread thread, int depth) {
123221
try {
124222
IStackFrame[] jdiStackFrames = thread.getStackFrames();
@@ -188,19 +286,19 @@ public void clearState(ThreadReference thread) {
188286
}
189287
}
190288

191-
private void ensureDebugTarget(VirtualMachine vm) {
289+
private void ensureDebugTarget(VirtualMachine vm, ThreadReference thread, int depth) {
192290
if (debugTarget == null) {
193-
String projectName = (String) options.get(Constants.PROJECTNAME);
194291
if (project == null) {
292+
String projectName = (String) options.get(Constants.PROJECT_NAME);
195293
if (StringUtils.isBlank(projectName)) {
196-
logger.severe("Cannot evaluate when project is not specified.");
197-
throw new IllegalStateException("Please specify projectName in launch.json.");
198-
}
199-
IJavaProject javaProject = JdtUtils.getJavaProject(projectName);
200-
if (javaProject == null) {
201-
throw new IllegalStateException(String.format("Project %s cannot be found.", projectName));
294+
findJavaProjectByStackFrame(thread, depth);
295+
} else {
296+
IJavaProject javaProject = JdtUtils.getJavaProject(projectName);
297+
if (javaProject == null) {
298+
throw new IllegalStateException(String.format("Project %s cannot be found.", projectName));
299+
}
300+
project = javaProject;
202301
}
203-
project = javaProject;
204302
}
205303

206304
if (launch == null) {

0 commit comments

Comments
 (0)