Skip to content

Commit 6633a55

Browse files
Fix the stack frame conflicts between logical structure and evaluation (#270)
* Fix the stack frame conflicts between logical structure and evaluation Signed-off-by: Jinbo Wang <[email protected]> * Use JDIThread.invokeMethod approach to handle the invalid stack frame issue Signed-off-by: Jinbo Wang <[email protected]>
1 parent b8e9795 commit 6633a55

File tree

6 files changed

+88
-75
lines changed

6 files changed

+88
-75
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,20 @@ public interface IEvaluationProvider extends IProvider {
6262
*/
6363
CompletableFuture<Value> evaluateForBreakpoint(IEvaluatableBreakpoint breakpoint, ThreadReference thread);
6464

65+
/**
66+
* Invoke the specified method with the given arguments at this object and the given thread, and return the result.
67+
* The given thread is resumed to perform the method invocation. The thread will suspend in its originallocation when the method invocation is complete.
68+
* @param thisContext The 'this' context for the invocation
69+
* @param methodName The method to be invoked
70+
* @param methodSignature The JNI style signature of the method to be invoked
71+
* @param args The arguments of the method, which can be null or empty if there are none
72+
* @param thread The thread in which to invoke the method
73+
* @param invokeSuper true if the method lookup should begin in thisobject's superclass
74+
* @return The result of invoking the method
75+
*/
76+
CompletableFuture<Value> invokeMethod(ObjectReference thisContext, String methodName, String methodSignature,
77+
Value[] args, ThreadReference thread, boolean invokeSuper);
78+
6579
/**
6680
* Call this method when the thread is to be resumed by user, it will first cancel ongoing evaluation tasks on specified thread and
6781
* ensure the inner states is cleaned.

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

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.util.Arrays;
1515
import java.util.List;
1616
import java.util.Map;
17+
import java.util.concurrent.CancellationException;
1718
import java.util.concurrent.CompletableFuture;
1819
import java.util.concurrent.CompletionException;
1920
import java.util.concurrent.ExecutionException;
@@ -41,11 +42,7 @@
4142
import com.microsoft.java.debug.core.protocol.Requests.EvaluateArguments;
4243
import com.microsoft.java.debug.core.protocol.Responses;
4344
import com.sun.jdi.ArrayReference;
44-
import com.sun.jdi.ClassNotLoadedException;
45-
import com.sun.jdi.IncompatibleThreadStateException;
4645
import com.sun.jdi.IntegerValue;
47-
import com.sun.jdi.InvalidTypeException;
48-
import com.sun.jdi.InvocationException;
4946
import com.sun.jdi.ObjectReference;
5047
import com.sun.jdi.Value;
5148
import com.sun.jdi.VoidValue;
@@ -80,8 +77,8 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
8077
}
8178

8279
return CompletableFuture.supplyAsync(() -> {
80+
IEvaluationProvider engine = context.getProvider(IEvaluationProvider.class);
8381
try {
84-
IEvaluationProvider engine = context.getProvider(IEvaluationProvider.class);
8582
Value value = engine.evaluate(expression, stackFrameReference.getThread(), stackFrameReference.getDepth()).get();
8683
IVariableFormatter variableFormatter = context.getVariableFormatter();
8784
if (value instanceof VoidValue) {
@@ -102,8 +99,8 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
10299
if (sizeValue != null && sizeValue instanceof IntegerValue) {
103100
indexedVariables = ((IntegerValue) sizeValue).value();
104101
}
105-
} catch (InvalidTypeException | ClassNotLoadedException | IncompatibleThreadStateException
106-
| InvocationException | InterruptedException | ExecutionException | UnsupportedOperationException e) {
102+
} catch (CancellationException | IllegalArgumentException | InterruptedException
103+
| ExecutionException | UnsupportedOperationException e) {
107104
logger.log(Level.INFO,
108105
String.format("Failed to get the logical size for the type %s.", value.type().name()), e);
109106
}

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

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.List;
2020
import java.util.Map;
2121
import java.util.Set;
22+
import java.util.concurrent.CancellationException;
2223
import java.util.concurrent.CompletableFuture;
2324
import java.util.concurrent.ExecutionException;
2425
import java.util.logging.Level;
@@ -50,13 +51,9 @@
5051
import com.microsoft.java.debug.core.protocol.Types;
5152
import com.sun.jdi.AbsentInformationException;
5253
import com.sun.jdi.ArrayReference;
53-
import com.sun.jdi.ClassNotLoadedException;
54-
import com.sun.jdi.IncompatibleThreadStateException;
5554
import com.sun.jdi.IntegerValue;
5655
import com.sun.jdi.InternalException;
5756
import com.sun.jdi.InvalidStackFrameException;
58-
import com.sun.jdi.InvalidTypeException;
59-
import com.sun.jdi.InvocationException;
6057
import com.sun.jdi.ObjectReference;
6158
import com.sun.jdi.StackFrame;
6259
import com.sun.jdi.Type;
@@ -147,8 +144,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
147144
childrenList.add(new Variable(name, value));
148145
}
149146
}
150-
} catch (InterruptedException | ExecutionException | InvalidTypeException
151-
| ClassNotLoadedException | IncompatibleThreadStateException | InvocationException e) {
147+
} catch (IllegalArgumentException | CancellationException | InterruptedException | ExecutionException e) {
152148
logger.log(Level.WARNING,
153149
String.format("Failed to get the logical structure for the type %s, fall back to the Object view.",
154150
containerObj.type().name()),
@@ -226,8 +222,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
226222
if (sizeValue != null && sizeValue instanceof IntegerValue) {
227223
indexedVariables = ((IntegerValue) sizeValue).value();
228224
}
229-
} catch (InvalidTypeException | ClassNotLoadedException | IncompatibleThreadStateException
230-
| InvocationException | InterruptedException | ExecutionException | UnsupportedOperationException e) {
225+
} catch (CancellationException | IllegalArgumentException | InterruptedException | ExecutionException | UnsupportedOperationException e) {
231226
logger.log(Level.INFO,
232227
String.format("Failed to get the logical size for the type %s.", value.type().name()), e);
233228
}
@@ -245,6 +240,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
245240
list.add(typedVariables);
246241
}
247242
response.body = new Responses.VariablesResponseBody(list);
243+
248244
return CompletableFuture.completedFuture(response);
249245
}
250246

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructure.java

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

1212
package com.microsoft.java.debug.core.adapter.variables;
1313

14-
import java.util.Collections;
1514
import java.util.List;
1615
import java.util.Objects;
16+
import java.util.concurrent.CancellationException;
1717
import java.util.concurrent.ExecutionException;
1818

1919
import com.microsoft.java.debug.core.adapter.IEvaluationProvider;
20-
import com.sun.jdi.ClassNotLoadedException;
2120
import com.sun.jdi.ClassType;
2221
import com.sun.jdi.Field;
23-
import com.sun.jdi.IncompatibleThreadStateException;
2422
import com.sun.jdi.InterfaceType;
25-
import com.sun.jdi.InvalidTypeException;
26-
import com.sun.jdi.InvocationException;
27-
import com.sun.jdi.Method;
2823
import com.sun.jdi.ObjectReference;
2924
import com.sun.jdi.ThreadReference;
3025
import com.sun.jdi.Type;
@@ -95,8 +90,7 @@ public boolean providesLogicalStructure(ObjectReference obj) {
9590
* Return the logical size of the specified thisObject.
9691
*/
9792
public Value getSize(ObjectReference thisObject, ThreadReference thread, IEvaluationProvider evaluationEngine)
98-
throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException,
99-
InterruptedException, ExecutionException, UnsupportedOperationException {
93+
throws CancellationException, InterruptedException, IllegalArgumentException, ExecutionException, UnsupportedOperationException {
10094
if (sizeExpression == null) {
10195
throw new UnsupportedOperationException("The object hasn't defined the logical size operation.");
10296
}
@@ -108,52 +102,32 @@ public Value getSize(ObjectReference thisObject, ThreadReference thread, IEvalua
108102
* Return the logical value of the specified thisObject.
109103
*/
110104
public Value getValue(ObjectReference thisObject, ThreadReference thread, IEvaluationProvider evaluationEngine)
111-
throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException,
112-
InterruptedException, ExecutionException {
105+
throws CancellationException, IllegalArgumentException, InterruptedException, ExecutionException {
113106
return getValue(thisObject, valueExpression, thread, evaluationEngine);
114107
}
115108

116109
private static Value getValue(ObjectReference thisObject, LogicalStructureExpression expression, ThreadReference thread,
117-
IEvaluationProvider evaluationEngine)
118-
throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException,
119-
InterruptedException, ExecutionException {
110+
IEvaluationProvider evaluationEngine) throws CancellationException, IllegalArgumentException, InterruptedException, ExecutionException {
120111
if (expression.type == LogicalStructureExpressionType.METHOD) {
121-
return getValueByMethod(thisObject, expression.value, thread);
112+
if (expression.value == null || expression.value.length < 2) {
113+
throw new IllegalArgumentException("The method expression should contain at least methodName and methodSignature!");
114+
}
115+
return evaluationEngine.invokeMethod(thisObject, expression.value[0], expression.value[1], null, thread, false).get();
122116
} else if (expression.type == LogicalStructureExpressionType.FIELD) {
123-
return getValueByField(thisObject, expression.value, thread);
117+
if (expression.value == null || expression.value.length < 1) {
118+
throw new IllegalArgumentException("The field expression should contain the field name!");
119+
}
120+
return getValueByField(thisObject, expression.value[0], thread);
124121
} else {
125-
return evaluationEngine.evaluate(expression.value, thisObject, thread).get();
126-
}
127-
}
128-
129-
private static Value getValueByMethod(ObjectReference thisObject, String methodName, ThreadReference thread)
130-
throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException {
131-
List<Method> methods = thisObject.referenceType().allMethods();
132-
Method targetMethod = null;
133-
for (Method method : methods) {
134-
if (Objects.equals(method.name(), methodName) && method.argumentTypeNames().isEmpty()) {
135-
targetMethod = method;
136-
break;
122+
if (expression.value == null || expression.value.length < 1) {
123+
throw new IllegalArgumentException("The evaluation expression should contain a valid expression statement!");
137124
}
125+
return evaluationEngine.evaluate(expression.value[0], thisObject, thread).get();
138126
}
139-
140-
if (targetMethod == null) {
141-
return null;
142-
}
143-
144-
return thisObject.invokeMethod(thread, targetMethod, Collections.EMPTY_LIST, ObjectReference.INVOKE_SINGLE_THREADED);
145127
}
146128

147-
private static Value getValueByField(ObjectReference thisObject, String filedName, ThreadReference thread) {
148-
List<Field> fields = thisObject.referenceType().allFields();
149-
Field targetField = null;
150-
for (Field field : fields) {
151-
if (Objects.equals(field.name(), filedName)) {
152-
targetField = field;
153-
break;
154-
}
155-
}
156-
129+
private static Value getValueByField(ObjectReference thisObject, String fieldName, ThreadReference thread) {
130+
Field targetField = thisObject.referenceType().fieldByName(fieldName);
157131
if (targetField == null) {
158132
return null;
159133
}
@@ -175,20 +149,19 @@ public String getName() {
175149
}
176150

177151
public Value getValue(ObjectReference thisObject, ThreadReference thread, IEvaluationProvider evaluationEngine)
178-
throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException,
179-
InterruptedException, ExecutionException {
152+
throws CancellationException, IllegalArgumentException, InterruptedException, ExecutionException {
180153
return JavaLogicalStructure.getValue(thisObject, valueExpression, thread, evaluationEngine);
181154
}
182155
}
183156

184157
public static class LogicalStructureExpression {
185158
public LogicalStructureExpressionType type;
186-
public String value;
159+
public String[] value;
187160

188161
/**
189162
* Constructor.
190163
*/
191-
public LogicalStructureExpression(LogicalStructureExpressionType type, String value) {
164+
public LogicalStructureExpression(LogicalStructureExpressionType type, String[] value) {
192165
this.type = type;
193166
this.value = value;
194167
}

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructureManager.java

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,13 @@
1414
import java.util.ArrayList;
1515
import java.util.Collections;
1616
import java.util.List;
17+
import java.util.concurrent.CancellationException;
1718
import java.util.concurrent.ExecutionException;
1819

1920
import com.microsoft.java.debug.core.adapter.IEvaluationProvider;
2021
import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure.LogicalStructureExpression;
2122
import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure.LogicalStructureExpressionType;
2223
import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure.LogicalVariable;
23-
import com.sun.jdi.ClassNotLoadedException;
24-
import com.sun.jdi.IncompatibleThreadStateException;
25-
import com.sun.jdi.InvalidTypeException;
26-
import com.sun.jdi.InvocationException;
2724
import com.sun.jdi.ObjectReference;
2825
import com.sun.jdi.ThreadReference;
2926
import com.sun.jdi.Value;
@@ -33,16 +30,17 @@ public class JavaLogicalStructureManager {
3330

3431
static {
3532
supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Map",
36-
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, "entrySet"),
37-
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, "size"),
33+
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"entrySet", "()Ljava/util/Set;"}),
34+
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"size", "()I"}),
3835
new LogicalVariable[0]));
3936
supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Map$Entry", null, null, new LogicalVariable[] {
40-
new LogicalVariable("key", new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, "getKey")),
41-
new LogicalVariable("value", new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, "getValue"))
37+
new LogicalVariable("key", new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"getKey", "()Ljava/lang/Object;"})),
38+
new LogicalVariable("value",
39+
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"getValue", "()Ljava/lang/Object;"}))
4240
}));
4341
supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Collection",
44-
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, "toArray"),
45-
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, "size"),
42+
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"toArray", "()[Ljava/lang/Object;"}),
43+
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"size", "()I"}),
4644
new LogicalVariable[0]));
4745
}
4846

@@ -71,8 +69,7 @@ public static boolean isIndexedVariable(ObjectReference obj) {
7169
* Return the logical size if the specified Object has defined the logical size.
7270
*/
7371
public static Value getLogicalSize(ObjectReference thisObject, ThreadReference thread, IEvaluationProvider evaluationEngine)
74-
throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException,
75-
InterruptedException, ExecutionException, UnsupportedOperationException {
72+
throws CancellationException, InterruptedException, IllegalArgumentException, ExecutionException, UnsupportedOperationException {
7673
JavaLogicalStructure structure = getLogicalStructure(thisObject);
7774
if (structure == null) {
7875
return null;

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

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import java.util.ArrayList;
1515
import java.util.Arrays;
16+
import java.util.Collections;
1617
import java.util.HashMap;
1718
import java.util.HashSet;
1819
import java.util.List;
@@ -41,11 +42,13 @@
4142
import org.eclipse.jdt.debug.core.IJavaObject;
4243
import org.eclipse.jdt.debug.core.IJavaStackFrame;
4344
import org.eclipse.jdt.debug.core.IJavaThread;
45+
import org.eclipse.jdt.debug.core.IJavaValue;
4446
import org.eclipse.jdt.debug.eval.ICompiledExpression;
4547
import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
4648
import org.eclipse.jdt.internal.debug.core.model.JDIObjectValue;
4749
import org.eclipse.jdt.internal.debug.core.model.JDIStackFrame;
4850
import org.eclipse.jdt.internal.debug.core.model.JDIThread;
51+
import org.eclipse.jdt.internal.debug.core.model.JDIValue;
4952
import org.eclipse.jdt.internal.debug.eval.ast.engine.ASTEvaluationEngine;
5053
import org.eclipse.jdt.internal.launching.JavaSourceLookupDirector;
5154

@@ -191,6 +194,34 @@ private CompletableFuture<Value> evaluate(String expression, ThreadReference thr
191194
}
192195
}
193196

197+
@Override
198+
public CompletableFuture<Value> invokeMethod(ObjectReference thisContext, String methodName, String methodSignature,
199+
Value[] args, ThreadReference thread, boolean invokeSuper) {
200+
CompletableFuture<Value> completableFuture = new CompletableFuture<>();
201+
try {
202+
ensureDebugTarget(thisContext.virtualMachine(), thisContext.type().name());
203+
JDIThread jdiThread = getMockJDIThread(thread);
204+
JDIObjectValue jdiObject = new JDIObjectValue(debugTarget, thisContext);
205+
List<IJavaValue> arguments = null;
206+
if (args == null) {
207+
arguments = Collections.EMPTY_LIST;
208+
} else {
209+
arguments = new ArrayList<>(args.length);
210+
for (Value arg : args) {
211+
arguments.add(new JDIValue(debugTarget, arg));
212+
}
213+
}
214+
IJavaValue javaValue = jdiObject.sendMessage(methodName, methodSignature, arguments.toArray(new IJavaValue[0]), jdiThread, invokeSuper);
215+
// we need to read fValue from the result Value instance implements by JDT
216+
Value value = (Value) FieldUtils.readField(javaValue, "fValue", true);
217+
completableFuture.complete(value);
218+
return completableFuture;
219+
} catch (Exception ex) {
220+
completableFuture.completeExceptionally(ex);
221+
return completableFuture;
222+
}
223+
}
224+
194225
private String logMessageToExpression(String logMessage) {
195226
final String LOGMESSAGE_VARIABLE_REGEXP = "\\{(.*?)\\}";
196227
String format = logMessage.replaceAll(LOGMESSAGE_VARIABLE_REGEXP, "%s");
@@ -348,7 +379,12 @@ private void internalEvaluate(ASTEvaluationEngine engine, ICompiledExpression co
348379

349380
@Override
350381
public boolean isInEvaluation(ThreadReference thread) {
351-
return debugTarget != null && getMockJDIThread(thread).isPerformingEvaluation();
382+
if (debugTarget == null) {
383+
return false;
384+
}
385+
386+
JDIThread jdiThread = getMockJDIThread(thread);
387+
return jdiThread != null && (jdiThread.isPerformingEvaluation() || jdiThread.isInvokingMethod());
352388
}
353389

354390
@Override

0 commit comments

Comments
 (0)