diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IEvaluationProvider.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IEvaluationProvider.java index 30ef1768a..8cece2f58 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IEvaluationProvider.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IEvaluationProvider.java @@ -62,6 +62,20 @@ public interface IEvaluationProvider extends IProvider { */ CompletableFuture 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 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. diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java index f194558af..f4c516fde 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java @@ -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; @@ -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; @@ -80,8 +77,8 @@ public CompletableFuture 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) { @@ -102,8 +99,8 @@ public CompletableFuture 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); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java index 2cde410b7..8ad042fcd 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java @@ -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; @@ -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; @@ -147,8 +144,7 @@ public CompletableFuture 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()), @@ -226,8 +222,7 @@ public CompletableFuture 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); } @@ -245,6 +240,7 @@ public CompletableFuture handle(Command command, Arguments arguments, list.add(typedVariables); } response.body = new Responses.VariablesResponseBody(list); + return CompletableFuture.completedFuture(response); } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructure.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructure.java index 89040d822..c186e0dc9 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructure.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructure.java @@ -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; @@ -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."); } @@ -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 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 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; } @@ -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; } diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructureManager.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructureManager.java index c67acd7bc..45b7a7974 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructureManager.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructureManager.java @@ -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; @@ -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])); } @@ -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; 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 3cd66eaf4..ceb93c5cf 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,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; @@ -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; @@ -191,6 +194,34 @@ private CompletableFuture evaluate(String expression, ThreadReference thr } } + @Override + public CompletableFuture invokeMethod(ObjectReference thisContext, String methodName, String methodSignature, + Value[] args, ThreadReference thread, boolean invokeSuper) { + CompletableFuture completableFuture = new CompletableFuture<>(); + try { + ensureDebugTarget(thisContext.virtualMachine(), thisContext.type().name()); + JDIThread jdiThread = getMockJDIThread(thread); + JDIObjectValue jdiObject = new JDIObjectValue(debugTarget, thisContext); + List 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"); @@ -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