Skip to content

Commit

Permalink
Fix the stack frame conflicts between logical structure and evaluation (
Browse files Browse the repository at this point in the history
#270)

* Fix the stack frame conflicts between logical structure and evaluation

Signed-off-by: Jinbo Wang <jinbwan@microsoft.com>

* Use JDIThread.invokeMethod approach to handle the invalid stack frame issue

Signed-off-by: Jinbo Wang <jinbwan@microsoft.com>
  • Loading branch information
testforstephen authored Apr 22, 2019
1 parent b8e9795 commit 6633a55
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ public interface IEvaluationProvider extends IProvider {
*/
CompletableFuture<Value> evaluateForBreakpoint(IEvaluatableBreakpoint breakpoint, ThreadReference thread);

/**
* Invoke the specified method with the given arguments at this object and the given thread, and return the result.
* The given thread is resumed to perform the method invocation. The thread will suspend in its originallocation when the method invocation is complete.
* @param thisContext The 'this' context for the invocation
* @param methodName The method to be invoked
* @param methodSignature The JNI style signature of the method to be invoked
* @param args The arguments of the method, which can be null or empty if there are none
* @param thread The thread in which to invoke the method
* @param invokeSuper true if the method lookup should begin in thisobject's superclass
* @return The result of invoking the method
*/
CompletableFuture<Value> invokeMethod(ObjectReference thisContext, String methodName, String methodSignature,
Value[] args, ThreadReference thread, boolean invokeSuper);

/**
* Call this method when the thread is to be resumed by user, it will first cancel ongoing evaluation tasks on specified thread and
* ensure the inner states is cleaned.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
Expand Down Expand Up @@ -41,11 +42,7 @@
import com.microsoft.java.debug.core.protocol.Requests.EvaluateArguments;
import com.microsoft.java.debug.core.protocol.Responses;
import com.sun.jdi.ArrayReference;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.IntegerValue;
import com.sun.jdi.InvalidTypeException;
import com.sun.jdi.InvocationException;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.Value;
import com.sun.jdi.VoidValue;
Expand Down Expand Up @@ -80,8 +77,8 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
}

return CompletableFuture.supplyAsync(() -> {
IEvaluationProvider engine = context.getProvider(IEvaluationProvider.class);
try {
IEvaluationProvider engine = context.getProvider(IEvaluationProvider.class);
Value value = engine.evaluate(expression, stackFrameReference.getThread(), stackFrameReference.getDepth()).get();
IVariableFormatter variableFormatter = context.getVariableFormatter();
if (value instanceof VoidValue) {
Expand All @@ -102,8 +99,8 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
if (sizeValue != null && sizeValue instanceof IntegerValue) {
indexedVariables = ((IntegerValue) sizeValue).value();
}
} catch (InvalidTypeException | ClassNotLoadedException | IncompatibleThreadStateException
| InvocationException | InterruptedException | ExecutionException | UnsupportedOperationException e) {
} catch (CancellationException | IllegalArgumentException | InterruptedException
| ExecutionException | UnsupportedOperationException e) {
logger.log(Level.INFO,
String.format("Failed to get the logical size for the type %s.", value.type().name()), e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
Expand Down Expand Up @@ -50,13 +51,9 @@
import com.microsoft.java.debug.core.protocol.Types;
import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.ArrayReference;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.IntegerValue;
import com.sun.jdi.InternalException;
import com.sun.jdi.InvalidStackFrameException;
import com.sun.jdi.InvalidTypeException;
import com.sun.jdi.InvocationException;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.StackFrame;
import com.sun.jdi.Type;
Expand Down Expand Up @@ -147,8 +144,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
childrenList.add(new Variable(name, value));
}
}
} catch (InterruptedException | ExecutionException | InvalidTypeException
| ClassNotLoadedException | IncompatibleThreadStateException | InvocationException e) {
} catch (IllegalArgumentException | CancellationException | InterruptedException | ExecutionException e) {
logger.log(Level.WARNING,
String.format("Failed to get the logical structure for the type %s, fall back to the Object view.",
containerObj.type().name()),
Expand Down Expand Up @@ -226,8 +222,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
if (sizeValue != null && sizeValue instanceof IntegerValue) {
indexedVariables = ((IntegerValue) sizeValue).value();
}
} catch (InvalidTypeException | ClassNotLoadedException | IncompatibleThreadStateException
| InvocationException | InterruptedException | ExecutionException | UnsupportedOperationException e) {
} catch (CancellationException | IllegalArgumentException | InterruptedException | ExecutionException | UnsupportedOperationException e) {
logger.log(Level.INFO,
String.format("Failed to get the logical size for the type %s.", value.type().name()), e);
}
Expand All @@ -245,6 +240,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
list.add(typedVariables);
}
response.body = new Responses.VariablesResponseBody(list);

return CompletableFuture.completedFuture(response);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,15 @@

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

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;

import com.microsoft.java.debug.core.adapter.IEvaluationProvider;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.ClassType;
import com.sun.jdi.Field;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.InterfaceType;
import com.sun.jdi.InvalidTypeException;
import com.sun.jdi.InvocationException;
import com.sun.jdi.Method;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.Type;
Expand Down Expand Up @@ -95,8 +90,7 @@ public boolean providesLogicalStructure(ObjectReference obj) {
* Return the logical size of the specified thisObject.
*/
public Value getSize(ObjectReference thisObject, ThreadReference thread, IEvaluationProvider evaluationEngine)
throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException,
InterruptedException, ExecutionException, UnsupportedOperationException {
throws CancellationException, InterruptedException, IllegalArgumentException, ExecutionException, UnsupportedOperationException {
if (sizeExpression == null) {
throw new UnsupportedOperationException("The object hasn't defined the logical size operation.");
}
Expand All @@ -108,52 +102,32 @@ public Value getSize(ObjectReference thisObject, ThreadReference thread, IEvalua
* Return the logical value of the specified thisObject.
*/
public Value getValue(ObjectReference thisObject, ThreadReference thread, IEvaluationProvider evaluationEngine)
throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException,
InterruptedException, ExecutionException {
throws CancellationException, IllegalArgumentException, InterruptedException, ExecutionException {
return getValue(thisObject, valueExpression, thread, evaluationEngine);
}

private static Value getValue(ObjectReference thisObject, LogicalStructureExpression expression, ThreadReference thread,
IEvaluationProvider evaluationEngine)
throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException,
InterruptedException, ExecutionException {
IEvaluationProvider evaluationEngine) throws CancellationException, IllegalArgumentException, InterruptedException, ExecutionException {
if (expression.type == LogicalStructureExpressionType.METHOD) {
return getValueByMethod(thisObject, expression.value, thread);
if (expression.value == null || expression.value.length < 2) {
throw new IllegalArgumentException("The method expression should contain at least methodName and methodSignature!");
}
return evaluationEngine.invokeMethod(thisObject, expression.value[0], expression.value[1], null, thread, false).get();
} else if (expression.type == LogicalStructureExpressionType.FIELD) {
return getValueByField(thisObject, expression.value, thread);
if (expression.value == null || expression.value.length < 1) {
throw new IllegalArgumentException("The field expression should contain the field name!");
}
return getValueByField(thisObject, expression.value[0], thread);
} else {
return evaluationEngine.evaluate(expression.value, thisObject, thread).get();
}
}

private static Value getValueByMethod(ObjectReference thisObject, String methodName, ThreadReference thread)
throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException {
List<Method> methods = thisObject.referenceType().allMethods();
Method targetMethod = null;
for (Method method : methods) {
if (Objects.equals(method.name(), methodName) && method.argumentTypeNames().isEmpty()) {
targetMethod = method;
break;
if (expression.value == null || expression.value.length < 1) {
throw new IllegalArgumentException("The evaluation expression should contain a valid expression statement!");
}
return evaluationEngine.evaluate(expression.value[0], thisObject, thread).get();
}

if (targetMethod == null) {
return null;
}

return thisObject.invokeMethod(thread, targetMethod, Collections.EMPTY_LIST, ObjectReference.INVOKE_SINGLE_THREADED);
}

private static Value getValueByField(ObjectReference thisObject, String filedName, ThreadReference thread) {
List<Field> fields = thisObject.referenceType().allFields();
Field targetField = null;
for (Field field : fields) {
if (Objects.equals(field.name(), filedName)) {
targetField = field;
break;
}
}

private static Value getValueByField(ObjectReference thisObject, String fieldName, ThreadReference thread) {
Field targetField = thisObject.referenceType().fieldByName(fieldName);
if (targetField == null) {
return null;
}
Expand All @@ -175,20 +149,19 @@ public String getName() {
}

public Value getValue(ObjectReference thisObject, ThreadReference thread, IEvaluationProvider evaluationEngine)
throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException,
InterruptedException, ExecutionException {
throws CancellationException, IllegalArgumentException, InterruptedException, ExecutionException {
return JavaLogicalStructure.getValue(thisObject, valueExpression, thread, evaluationEngine);
}
}

public static class LogicalStructureExpression {
public LogicalStructureExpressionType type;
public String value;
public String[] value;

/**
* Constructor.
*/
public LogicalStructureExpression(LogicalStructureExpressionType type, String value) {
public LogicalStructureExpression(LogicalStructureExpressionType type, String[] value) {
this.type = type;
this.value = value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,13 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;

import com.microsoft.java.debug.core.adapter.IEvaluationProvider;
import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure.LogicalStructureExpression;
import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure.LogicalStructureExpressionType;
import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure.LogicalVariable;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.InvalidTypeException;
import com.sun.jdi.InvocationException;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.Value;
Expand All @@ -33,16 +30,17 @@ public class JavaLogicalStructureManager {

static {
supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Map",
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, "entrySet"),
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, "size"),
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"entrySet", "()Ljava/util/Set;"}),
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"size", "()I"}),
new LogicalVariable[0]));
supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Map$Entry", null, null, new LogicalVariable[] {
new LogicalVariable("key", new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, "getKey")),
new LogicalVariable("value", new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, "getValue"))
new LogicalVariable("key", new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"getKey", "()Ljava/lang/Object;"})),
new LogicalVariable("value",
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"getValue", "()Ljava/lang/Object;"}))
}));
supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Collection",
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, "toArray"),
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, "size"),
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"toArray", "()[Ljava/lang/Object;"}),
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"size", "()I"}),
new LogicalVariable[0]));
}

Expand Down Expand Up @@ -71,8 +69,7 @@ public static boolean isIndexedVariable(ObjectReference obj) {
* Return the logical size if the specified Object has defined the logical size.
*/
public static Value getLogicalSize(ObjectReference thisObject, ThreadReference thread, IEvaluationProvider evaluationEngine)
throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException,
InterruptedException, ExecutionException, UnsupportedOperationException {
throws CancellationException, InterruptedException, IllegalArgumentException, ExecutionException, UnsupportedOperationException {
JavaLogicalStructure structure = getLogicalStructure(thisObject);
if (structure == null) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -41,11 +42,13 @@
import org.eclipse.jdt.debug.core.IJavaObject;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.debug.core.IJavaThread;
import org.eclipse.jdt.debug.core.IJavaValue;
import org.eclipse.jdt.debug.eval.ICompiledExpression;
import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
import org.eclipse.jdt.internal.debug.core.model.JDIObjectValue;
import org.eclipse.jdt.internal.debug.core.model.JDIStackFrame;
import org.eclipse.jdt.internal.debug.core.model.JDIThread;
import org.eclipse.jdt.internal.debug.core.model.JDIValue;
import org.eclipse.jdt.internal.debug.eval.ast.engine.ASTEvaluationEngine;
import org.eclipse.jdt.internal.launching.JavaSourceLookupDirector;

Expand Down Expand Up @@ -191,6 +194,34 @@ private CompletableFuture<Value> evaluate(String expression, ThreadReference thr
}
}

@Override
public CompletableFuture<Value> invokeMethod(ObjectReference thisContext, String methodName, String methodSignature,
Value[] args, ThreadReference thread, boolean invokeSuper) {
CompletableFuture<Value> completableFuture = new CompletableFuture<>();
try {
ensureDebugTarget(thisContext.virtualMachine(), thisContext.type().name());
JDIThread jdiThread = getMockJDIThread(thread);
JDIObjectValue jdiObject = new JDIObjectValue(debugTarget, thisContext);
List<IJavaValue> arguments = null;
if (args == null) {
arguments = Collections.EMPTY_LIST;
} else {
arguments = new ArrayList<>(args.length);
for (Value arg : args) {
arguments.add(new JDIValue(debugTarget, arg));
}
}
IJavaValue javaValue = jdiObject.sendMessage(methodName, methodSignature, arguments.toArray(new IJavaValue[0]), jdiThread, invokeSuper);
// we need to read fValue from the result Value instance implements by JDT
Value value = (Value) FieldUtils.readField(javaValue, "fValue", true);
completableFuture.complete(value);
return completableFuture;
} catch (Exception ex) {
completableFuture.completeExceptionally(ex);
return completableFuture;
}
}

private String logMessageToExpression(String logMessage) {
final String LOGMESSAGE_VARIABLE_REGEXP = "\\{(.*?)\\}";
String format = logMessage.replaceAll(LOGMESSAGE_VARIABLE_REGEXP, "%s");
Expand Down Expand Up @@ -348,7 +379,12 @@ private void internalEvaluate(ASTEvaluationEngine engine, ICompiledExpression co

@Override
public boolean isInEvaluation(ThreadReference thread) {
return debugTarget != null && getMockJDIThread(thread).isPerformingEvaluation();
if (debugTarget == null) {
return false;
}

JDIThread jdiThread = getMockJDIThread(thread);
return jdiThread != null && (jdiThread.isPerformingEvaluation() || jdiThread.isInvokingMethod());
}

@Override
Expand Down

0 comments on commit 6633a55

Please sign in to comment.