Skip to content

Support copying variable #338

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 14, 2020
Merged
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
@@ -88,7 +88,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
}
long threadId = stackFrameReference.getThread().uniqueID();
if (value instanceof ObjectReference) {
VariableProxy varProxy = new VariableProxy(stackFrameReference.getThread(), "eval", value);
VariableProxy varProxy = new VariableProxy(stackFrameReference.getThread(), "eval", value, null, expression);
int indexedVariables = -1;
Value sizeValue = null;
if (value instanceof ArrayReference) {
@@ -120,9 +120,13 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
detailsString = VariableDetailUtils.formatDetailsValue(value, stackFrameReference.getThread(), variableFormatter, options, engine);
}

response.body = new Responses.EvaluateResponseBody((detailsString == null) ? valueString : valueString + " " + detailsString,
referenceId, variableFormatter.typeToString(value == null ? null : value.type(), options),
Math.max(indexedVariables, 0));
if ("clipboard".equals(evalArguments.context) && detailsString != null) {
response.body = new Responses.EvaluateResponseBody(detailsString, -1, "String", 0);
} else {
response.body = new Responses.EvaluateResponseBody((detailsString == null) ? valueString : valueString + " " + detailsString,
referenceId, variableFormatter.typeToString(value == null ? null : value.type(), options),
Math.max(indexedVariables, 0));
}
return response;
}
// for primitive value
Original file line number Diff line number Diff line change
@@ -62,6 +62,7 @@ public CompletableFuture<Messages.Response> handle(Requests.Command command, Req
caps.exceptionBreakpointFilters = exceptionFilters;
caps.supportsExceptionInfoRequest = true;
caps.supportsDataBreakpoints = true;
caps.supportsClipboardContext = true;
response.body = caps;
return CompletableFuture.completedFuture(response);
}
Original file line number Diff line number Diff line change
@@ -45,7 +45,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
return CompletableFuture.completedFuture(response);
}
ThreadReference thread = stackFrameReference.getThread();
VariableProxy localScope = new VariableProxy(thread, "Local", stackFrameReference);
VariableProxy localScope = new VariableProxy(thread, "Local", stackFrameReference, null, null);
int localScopeId = context.getRecyclableIdPool().addObject(thread.uniqueID(), localScope);
scopes.add(new Types.Scope(localScope.getScope(), localScopeId, false));

Original file line number Diff line number Diff line change
@@ -121,7 +121,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
if (newValue instanceof ObjectReference && VariableUtils.hasChildren(newValue, showStaticVariables)) {
long threadId = ((VariableProxy) container).getThreadId();
String scopeName = ((VariableProxy) container).getScope();
VariableProxy varProxy = new VariableProxy(((VariableProxy) container).getThread(), scopeName, newValue);
VariableProxy varProxy = new VariableProxy(((VariableProxy) container).getThread(), scopeName, newValue, (VariableProxy) container, name);
referenceId = context.getRecyclableIdPool().addObject(threadId, varProxy);
}

Original file line number Diff line number Diff line change
@@ -98,6 +98,8 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
VariableProxy containerNode = (VariableProxy) container;
List<Variable> childrenList = new ArrayList<>();
IStackFrameManager stackFrameManager = context.getStackFrameManager();
String containerEvaluateName = containerNode.getEvaluateName();
boolean isUnboundedTypeContainer = containerNode.isUnboundedType();
if (containerNode.getProxiedVariable() instanceof StackFrameReference) {
StackFrameReference stackFrameReference = (StackFrameReference) containerNode.getProxiedVariable();
StackFrame frame = stackFrameManager.getStackFrame(stackFrameReference);
@@ -111,7 +113,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
JdiMethodResult result = context.getStepResultManager().getMethodResult(threadId);
if (result != null) {
String returnIcon = (AdapterUtils.isWin || AdapterUtils.isMac) ? "⎯►" : "->";
childrenList.add(new Variable(returnIcon + result.method.name() + "()", result.value));
childrenList.add(new Variable(returnIcon + result.method.name() + "()", result.value, null));
}
childrenList.addAll(VariableUtils.listLocalVariables(frame));
Variable thisVariable = VariableUtils.getThisVariable(frame);
@@ -132,11 +134,17 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
ObjectReference containerObj = (ObjectReference) containerNode.getProxiedVariable();
if (DebugSettings.getCurrent().showLogicalStructure && evaluationEngine != null) {
JavaLogicalStructure logicalStructure = JavaLogicalStructureManager.getLogicalStructure(containerObj);
if (isUnboundedTypeContainer && logicalStructure != null && containerEvaluateName != null) {
containerEvaluateName = "((" + logicalStructure.getFullyQualifiedName() + ")" + containerEvaluateName + ")";
isUnboundedTypeContainer = false;
}
while (logicalStructure != null) {
LogicalStructureExpression valueExpression = logicalStructure.getValueExpression();
LogicalVariable[] logicalVariables = logicalStructure.getVariables();
try {
if (valueExpression != null) {
containerEvaluateName = containerEvaluateName == null ? null : containerEvaluateName + "." + valueExpression.evaluateName;
isUnboundedTypeContainer = valueExpression.returnUnboundedType;
Value value = logicalStructure.getValue(containerObj, containerNode.getThread(), evaluationEngine);
if (value instanceof ObjectReference) {
containerObj = (ObjectReference) value;
@@ -149,7 +157,9 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
for (LogicalVariable logicalVariable : logicalVariables) {
String name = logicalVariable.getName();
Value value = logicalVariable.getValue(containerObj, containerNode.getThread(), evaluationEngine);
childrenList.add(new Variable(name, value));
Variable variable = new Variable(name, value, logicalVariable.getEvaluateName());
variable.setUnboundedType(logicalVariable.returnUnboundedType());
childrenList.add(variable);
}
}
} catch (IllegalArgumentException | CancellationException | InterruptedException | ExecutionException e) {
@@ -236,15 +246,37 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
}
}

String evaluateName = null;
if (javaVariable.evaluateName == null || (containerEvaluateName == null && containerNode.getProxiedVariable() instanceof ObjectReference)) {
// Disable evaluate on the method return value.
evaluateName = null;
} else if (isUnboundedTypeContainer && !containerNode.isIndexedVariable()) {
// The type name returned by JDI is the binary name, which uses '$' as the separator of
// inner class e.g. Foo$Bar. But the evaluation expression only accepts using '.' as the class
// name separator.
String typeName = ((ObjectReference) containerNode.getProxiedVariable()).referenceType().name();
// TODO: This replacement will possibly change the $ in the class name itself.
typeName = typeName.replaceAll("\\$", ".");
evaluateName = VariableUtils.getEvaluateName(javaVariable.evaluateName, "((" + typeName + ")" + containerEvaluateName + ")", false);
} else {
if (containerEvaluateName != null && containerEvaluateName.contains("%s")) {
evaluateName = String.format(containerEvaluateName, javaVariable.evaluateName);
} else {
evaluateName = VariableUtils.getEvaluateName(javaVariable.evaluateName, containerEvaluateName, containerNode.isIndexedVariable());
}
}

int referenceId = 0;
if (indexedVariables > 0 || (indexedVariables < 0 && VariableUtils.hasChildren(value, showStaticVariables))) {
VariableProxy varProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), value);
VariableProxy varProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), value, containerNode, evaluateName);
referenceId = context.getRecyclableIdPool().addObject(containerNode.getThreadId(), varProxy);
varProxy.setIndexedVariable(indexedVariables >= 0);
varProxy.setUnboundedType(javaVariable.isUnboundedType());
}

Types.Variable typedVariables = new Types.Variable(name, variableFormatter.valueToString(value, options),
variableFormatter.typeToString(value == null ? null : value.type(), options),
referenceId, null);
referenceId, evaluateName);
typedVariables.indexedVariables = Math.max(indexedVariables, 0);
String detailsValue = null;
if (sizeValue != null) {
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019 Microsoft Corporation and others.
* Copyright (c) 2019-2020 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -26,7 +26,10 @@
import com.sun.jdi.Value;

public class JavaLogicalStructure {
// The binary type name. For inner type, the binary name uses '$' as the separator, e.g. java.util.Map$Entry.
private final String type;
// The fully qualified name, which uses '.' as the separator, e.g. java.util.Map.Entry.
private final String fullyQualifiedName;
private final LogicalStructureExpression valueExpression;
private final LogicalStructureExpression sizeExpression;
private final LogicalVariable[] variables;
@@ -36,8 +39,17 @@ public class JavaLogicalStructure {
*/
public JavaLogicalStructure(String type, LogicalStructureExpression valueExpression, LogicalStructureExpression sizeExpression,
LogicalVariable[] variables) {
this(type, type, valueExpression, sizeExpression, variables);
}

/**
* Constructor.
*/
public JavaLogicalStructure(String type, String fullyQualifiedName, LogicalStructureExpression valueExpression, LogicalStructureExpression sizeExpression,
LogicalVariable[] variables) {
this.valueExpression = valueExpression;
this.type = type;
this.fullyQualifiedName = fullyQualifiedName;
this.sizeExpression = sizeExpression;
this.variables = variables;
}
@@ -46,6 +58,10 @@ public String getType() {
return type;
}

public String getFullyQualifiedName() {
return fullyQualifiedName;
}

public LogicalStructureExpression getValueExpression() {
return valueExpression;
}
@@ -152,18 +168,50 @@ public Value getValue(ObjectReference thisObject, ThreadReference thread, IEvalu
throws CancellationException, IllegalArgumentException, InterruptedException, ExecutionException {
return JavaLogicalStructure.getValue(thisObject, valueExpression, thread, evaluationEngine);
}

public String getEvaluateName() {
if (valueExpression == null || valueExpression.evaluateName == null) {
return name;
}

return valueExpression.evaluateName;
}

public boolean returnUnboundedType() {
return valueExpression != null && valueExpression.returnUnboundedType;
}
}

public static class LogicalStructureExpression {
public LogicalStructureExpressionType type;
public String[] value;
public String evaluateName;
public boolean returnUnboundedType = false;

/**
* Constructor.
*/
public LogicalStructureExpression(LogicalStructureExpressionType type, String[] value) {
this(type, value, null);
}

/**
* Constructor.
*/
public LogicalStructureExpression(LogicalStructureExpressionType type, String[] value, String evaluateName) {
this.type = type;
this.value = value;
this.evaluateName = evaluateName;
}

/**
* Constructor.
*/
public LogicalStructureExpression(LogicalStructureExpressionType type, String[] value, String evaluateName, boolean returnUnboundedType) {
this.type = type;
this.value = value;
this.evaluateName = evaluateName;
this.returnUnboundedType = returnUnboundedType;
}
}

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019 Microsoft Corporation and others.
* Copyright (c) 2019-2020 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -30,18 +30,30 @@ public class JavaLogicalStructureManager {

static {
supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Map",
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, new String[] {"getKey", "()Ljava/lang/Object;"})),
new LogicalVariable("value",
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"getValue", "()Ljava/lang/Object;"}))
}));
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"entrySet", "()Ljava/util/Set;"}, "entrySet()"),
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"size", "()I"}),
new LogicalVariable[0]
));
supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Map$Entry", "java.util.Map.Entry", null, null,
new LogicalVariable[] {
new LogicalVariable("key",
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"getKey", "()Ljava/lang/Object;"}, "getKey()", true)
),
new LogicalVariable("value",
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD,
new String[] {"getValue", "()Ljava/lang/Object;"}, "getValue()", true)
)}
));
supportedLogicalStructures.add(new JavaLogicalStructure("java.util.List",
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"toArray", "()[Ljava/lang/Object;"}, "get(%s)", true),
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"size", "()I"}),
new LogicalVariable[0]
));
supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Collection",
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"toArray", "()[Ljava/lang/Object;"}),
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"size", "()I"}),
new LogicalVariable[0]));
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"toArray", "()[Ljava/lang/Object;"}, "toArray()", true),
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, new String[] {"size", "()I"}),
new LogicalVariable[0]
));
}

/**
Original file line number Diff line number Diff line change
@@ -13,6 +13,8 @@

import org.apache.commons.lang3.StringUtils;

import java.util.Objects;

import com.sun.jdi.Field;
import com.sun.jdi.LocalVariable;
import com.sun.jdi.Type;
@@ -61,18 +63,39 @@ public class Variable {
*/
public int argumentIndex;

/**
* The variable evaluate name for the container context. Defaults to the variable name.
*/
public String evaluateName;

/**
* Indicates whether this variable's type is determined at runtime.
*/
private boolean isUnboundedType = false;

/**
* The constructor of <code>JavaVariable</code>.
* @param name the name of this variable.
* @param value the JDI value
*/
public Variable(String name, Value value) {
this(name, value, name);
}

/**
* The constructor of <code>JavaVariable</code>.
* @param name the name of this variable.
* @param value the JDI value
* @param evaluateName the variable evaluate name for the container context if any
*/
public Variable(String name, Value value, String evaluateName) {
if (StringUtils.isBlank(name)) {
throw new IllegalArgumentException("Name is required for a java variable.");
}
this.name = name;
this.value = value;
this.argumentIndex = -1;
this.evaluateName = evaluateName;
}

/**
@@ -86,4 +109,16 @@ public Type getDeclaringType() {
}
return null;
}

public void setUnboundedType(boolean isUnboundedType) {
this.isUnboundedType = isUnboundedType;
}

public boolean isUnboundedType() {
if (isUnboundedType) {
return true;
}

return field != null && Objects.equals(field.signature(), "Ljava/lang/Object;");
}
}
Loading