8000 Variable: Show toString() value for the classes that override toString method by testforstephen · Pull Request #276 · microsoft/java-debug · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
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
Expand Up @@ -26,6 +26,7 @@ public final class DebugSettings {
public boolean showQualifiedNames = false;
public boolean showHex = false;
public boolean showLogicalStructure = true;
public boolean showToString = true;
public String logLevel;
public String javaHome;
public HotCodeReplace hotCodeReplace = HotCodeReplace.MANUAL;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017 Microsoft Corporation and others.
* Copyright (c) 2017-2019 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
Expand Down Expand Up @@ -39,7 +39,7 @@ public ObjectFormatter(BiFunction<Type, Map<String, Object>, String> typeToStrin

@Override
public String toString(Object obj, Map<String, Object> options) {
return String.format("%s %s", getPrefix((ObjectReference) obj, options),
return String.format("%s@%s", getPrefix((ObjectReference) obj, options),
getIdPostfix((ObjectReference) obj, options));
}

Expand Down Expand Up @@ -74,6 +74,6 @@ protected String getPrefix(ObjectReference value, Map<String, Object> options) {
}

protected static String getIdPostfix(ObjectReference obj, Map<String, Object> options) {
return String.format("(id=%s)", NumericFormatter.formatNumber(obj.uniqueID(), options));
return NumericFormatter.formatNumber(obj.uniqueID(), options);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017 Microsoft Corporation and others.
* Copyright (c) 2017-2019 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
Expand All @@ -19,7 +19,6 @@

import org.apache.commons.lang3.StringUtils;

import com.sun.jdi.ObjectReference;
import com.sun.jdi.StringReference;
import com.sun.jdi.Type;
import com.sun.jdi.Value;
Expand All @@ -43,9 +42,8 @@ public Map<String, Object> getDefaultOptions() {
@Override
public String toString(Object value, Map<String, Object> options) {
int maxLength = getMaxStringLength(options);
return String.format("\"%s\" %s",
maxLength > 0 ? StringUtils.abbreviate(((StringReference) value).value(), maxLength) : ((StringReference) value).value(),
getIdPostfix((ObjectReference) value, options));
return String.format("\"%s\"",
maxLength > 0 ? StringUtils.abbreviate(((StringReference) value).value(), maxLength) : ((StringReference) value).value());
}

@Override
Expand Down
8000
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017 Microsoft Corporation and others.
* Copyright (c) 2017-2019 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
Expand Down Expand Up @@ -34,6 +34,7 @@
import com.microsoft.java.debug.core.adapter.variables.IVariableFormatter;
import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructureManager;
import com.microsoft.java.debug.core.adapter.variables.StackFrameReference;
import com.microsoft.java.debug.core.adapter.variables.VariableDetailUtils;
import com.microsoft.java.debug.core.adapter.variables.VariableProxy;
import com.microsoft.java.debug.core.adapter.variables.VariableUtils;
import com.microsoft.java.debug.core.protocol.Messages.Response;
Expand Down Expand Up @@ -89,13 +90,14 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
if (value instanceof ObjectReference) {
VariableProxy varProxy = new VariableProxy(stackFrameReference.getThread(), "eval", value);
int indexedVariables = -1;
Value sizeValue = null;
if (value instanceof ArrayReference) {
indexedVariables = ((ArrayReference) value).length();
} else if (value instanceof ObjectReference && DebugSettings.getCurrent().showLogicalStructure
&& engine != null
&& JavaLogicalStructureManager.isIndexedVariable((ObjectReference) value)) {
try {
Value sizeValue = JavaLogicalStructureManager.getLogicalSize((ObjectReference) value, stackFrameReference.getThread(), engine);
sizeValue = JavaLogicalStructureManager.getLogicalSize((ObjectReference) value, stackFrameReference.getThread(), engine);
if (sizeValue != null && sizeValue instanceof IntegerValue) {
indexedVariables = ((IntegerValue) sizeValue).value();
}
Expand All @@ -110,7 +112,15 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
referenceId = context.getRecyclableIdPool().addObject(threadId, varProxy);
}

response.body = new Responses.EvaluateResponseBody(variableFormatter.valueToString(value, options),
String valueString = variableFormatter.valueToString(value, options);
String detailsString = null;
if (sizeValue != null) {
detailsString = "size=" + variableFormatter.valueToString(sizeValue, options);
} else if (DebugSettings.getCurrent().showToString) {
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));
return response;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructureManager;
import com.microsoft.java.debug.core.adapter.variables.StackFrameReference;
import com.microsoft.java.debug.core.adapter.variables.Variable;
import com.microsoft.java.debug.core.adapter.variables.VariableDetailUtils;
import com.microsoft.java.debug.core.adapter.variables.VariableProxy;
import com.microsoft.java.debug.core.adapter.variables.VariableUtils;
import com.microsoft.java.debug.core.protocol.Messages.Response;
Expand Down Expand Up @@ -76,6 +77,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,

Map<String, Object> options = variableFormatter.getDefaultOptions();
VariableUtils.applyFormatterOptions(options, varArgs.format != null && varArgs.format.hex);
IEvaluationProvider evaluationEngine = context.getProvider(IEvaluationProvider.class);

List<Types.Variable> list = new ArrayList<>();
Object container = context.getRecyclableIdPool().getObjectById(varArgs.variablesReference);
Expand Down Expand Up @@ -121,7 +123,6 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
} else {
try {
ObjectReference containerObj = (ObjectReference) containerNode.getProxiedVariable();
IEvaluationProvider evaluationEngine = context.getProvider(IEvaluationProvider.class);
if (DebugSettings.getCurrent().showLogicalStructure && evaluationEngine != null) {
JavaLogicalStructure logicalStructure = JavaLogicalStructureManager.getLogicalStructure(containerObj);
while (logicalStructure != null) {
Expand Down Expand Up @@ -211,14 +212,14 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
name = variableNameMap.get(javaVariable);
}
int indexedVariables = -1;
Value sizeValue = null;
if (value instanceof ArrayReference) {
indexedVariables = ((ArrayReference) value).length();
} else if (value instanceof ObjectReference && DebugSettings.getCurrent().showLogicalStructure
&& context.getProvider(IEvaluationProvider.class) != null
&& evaluationEngine != null
&& JavaLogicalStructureManager.isIndexedVariable((ObjectReference) value)) {
IEvaluationProvider evaluationEngine = context.getProvider(IEvaluationProvider.class);
try {
Value sizeValue = JavaLogicalStructureManager.getLogicalSize((ObjectReference) value, containerNode.getThread(), evaluationEngine);
sizeValue = JavaLogicalStructureManager.getLogicalSize((ObjectReference) value, containerNode.getThread(), evaluationEngine);
if (sizeValue != null && sizeValue instanceof IntegerValue) {
indexedVariables = ((IntegerValue) sizeValue).value();
}
Expand All @@ -233,10 +234,21 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
VariableProxy varProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), value);
referenceId = context.getRecyclableIdPool().addObject(containerNode.getThreadId(), varProxy);
}

Types.Variable typedVariables = new Types.Variable(name, variableFormatter.valueToString(value, options),
variableFormatter.typeToString(value == null ? null : value.type(), options),
referenceId, null);
typedVariables.indexedVariables = Math.max(indexedVariables, 0);
String detailsValue = null;
if (sizeValue != null) {
detailsValue = "size=" + variableFormatter.valueToString(sizeValue, options);
} else if (DebugSettings.getCurrent().showToString) {
detailsValue = VariableDetailUtils.formatDetailsValue(value, containerNode.getThread(), variableFormatter, options, evaluationEngine);
}

if (detailsValue != null) {
typedVariables.value = typedVariables.value + " " + detailsValue;
}
list.add(typedVariables);
}
response.body = new Responses.VariablesResponseBody(list);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*******************************************************************************
* Copyright (c) 2019 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Microsoft Corporation - initial API and implementation
*******************************************************************************/

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

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutionException;

import com.microsoft.java.debug.core.adapter.IEvaluationProvider;
import com.sun.jdi.ClassType;
import com.sun.jdi.InterfaceType;
import com.sun.jdi.Method;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.Type;
import com.sun.jdi.Value;

public class VariableDetailUtils {
private static final String STRING_TYPE = "java.lang.String";
private static final String TO_STRING_METHOD = "toString";
private static final String TO_STRING_METHOD_SIGNATURE = "()Ljava/lang/String;";
private static final String ENTRY_TYPE = "java.util.Map$Entry";
private static final String GET_KEY_METHOD = "getKey";
private static final String GET_KEY_METHOD_SIGNATURE = "()Ljava/lang/Object;";
private static final String GET_VALUE_METHOD = "getValue";
private static final String GET_VALUE_METHOD_SIGNATURE = "()Ljava/lang/Object;";
private static final Set<String> COLLECTION_TYPES = new HashSet(
Arrays.asList("java.util.Map", "java.util.Collection", "java.util.Map$Entry"));

/**
* Returns the details information for the specified variable.
*/
public static String formatDetailsValue(Value value, ThreadReference thread, IVariableFormatter variableFormatter, Map<String, Object> options,
IEvaluationProvider evaluationEngine) {
if (isClassType(value, STRING_TYPE)) {
// No need to show additional details information.
return null;
} else {
return computeToStringValue(value, thread, variableFormatter, options, evaluationEngine, true);
}
}

private static String computeToStringValue(Value value, ThreadReference thread, IVariableFormatter variableFormatter,
Map<String, Object> options, IEvaluationProvider evaluationEngine, boolean isFirstLevel) {
if (!(value instanceof ObjectReference) || evaluationEngine == null) {
return null;
}

String inheritedType = findInheritedType(value, COLLECTION_TYPES);
if (inheritedType != null) {
if (Objects.equals(inheritedType, ENTRY_TYPE)) {
try {
Value keyObject = evaluationEngine.invokeMethod((ObjectReference) value, GET_KEY_METHOD, GET_KEY_METHOD_SIGNATURE,
null, thread, false).get();
Value valueObject = evaluationEngine.invokeMethod((ObjectReference) value, GET_VALUE_METHOD, GET_VALUE_METHOD_SIGNATURE,
null, thread, false).get();
String toStringValue = computeToStringValue(keyObject, thread, variableFormatter, options, evaluationEngine, false)
+ ":"
+ computeToStringValue(valueObject, thread, variableFormatter, options, evaluationEngine, false);
if (!isFirstLevel) {
toStringValue = "\"" + toStringValue + "\"";
}

return toStringValue;
} catch (InterruptedException | ExecutionException e) {
// do nothing.
}
} else if (!isFirstLevel) {
return variableFormatter.valueToString(value, options);
}
} else if (containsToStringMethod((ObjectReference) value)) {
try {
Value toStringValue = evaluationEngine.invokeMethod((ObjectReference) value, TO_STRING_METHOD, TO_STRING_METHOD_SIGNATURE,
null, thread, false).get();
return variableFormatter.valueToString(toStringValue, options);
} catch (InterruptedException | ExecutionException e) {
// do nothing.
}
}

return null;
}

private static boolean containsToStringMethod(ObjectReference obj) {
ReferenceType refType = obj.referenceType();
if (refType instanceof ClassType) {
Method m = ((ClassType) refType).concreteMethodByName(TO_STRING_METHOD, TO_STRING_METHOD_SIGNATURE);
if (m != null) {
if (!Objects.equals("Ljava/lang/Object;", m.declaringType().signature())) {
return true;
}
}

for (InterfaceType iface : ((ClassType) refType).allInterfaces()) {
List<Method> matches = iface.methodsByName(TO_STRING_METHOD, TO_STRING_METHOD_SIGNATURE);
for (Method ifaceMethod : matches) {
if (!ifaceMethod.isAbstract()) {
return true;
}
}
}
}

return false;
}

private static String findInheritedType(Value value, Set<String> typeNames) {
if (!(value instanceof ObjectReference)) {
return null;
}

Type variableType = ((ObjectReference) value).type();
if (!(variableType instanceof ClassType)) {
return null;
}

ClassType classType = (ClassType) variableType;
while (classType != null) {
if (typeNames.contains(classType.name())) {
return classType.name();
}

classType = classType.superclass();
}

List<InterfaceType> interfaceTypes = ((ClassType) variableType).allInterfaces();
for (InterfaceType interfaceType : interfaceTypes) {
if (typeNames.contains(interfaceType.name())) {
return interfaceType.name();
}
}

return null;
}

private static boolean isClassType(Value value, String typeName) {
if (!(value instanceof ObjectReference)) {
return false;
}

return Objects.equals(((ObjectReference) value).type().name(), typeName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public void testAcceptType() throws Exception {
@Test
public void testToString() throws Exception {
Value arrays = this.getLocalValue("arrays");
assertEquals("Should be able to format array type.", String.format("int[1] (id=%d)",
assertEquals("Should be able to format array type.", String.format("int[1]@%d",
((ObjectReference) arrays).uniqueID()),
formatter.toString(arrays, new HashMap<>()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public void testValueOfNotUnsupported() {
@Test
public void testToString() throws Exception {
Value clazzValue = this.getLocalValue("b");
assertEquals("Should be able to format clazz type.", String.format("java.lang.Class (A) (id=%d)",
assertEquals("Should be able to format clazz type.", String.format("java.lang.Class (A)@%d",
((ObjectReference)clazzValue).uniqueID()),
formatter.toString(clazzValue, new HashMap<>()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public void testAcceptType() throws Exception {
@Test
public void testToStringDec() throws Exception {
ObjectReference or = this.getObjectReference("Foo");
assertEquals("Failed to format an object.", String.format("MockType (id=%d)", or.uniqueID()),
assertEquals("Failed to format an object.", String.format("MockType@%d", or.uniqueID()),
formatter.toString(or, new HashMap<>()));
}

Expand All @@ -72,7 +72,7 @@ public void testToStringHex() throws Exception {
ObjectReference or = this.getObjectReference("Foo");
Map<String, Object> options = formatter.getDefaultOptions();
options.put(NUMERIC_FORMAT_OPTION, NumericFormatEnum.HEX);
assertEquals("Failed to format an object.", String.format("MockType (id=%#x)", or.uniqueID()),
assertEquals("Failed to format an object.", String.format("MockType@%#x", or.uniqueID()),
formatter.toString(or, options));
}

Expand All @@ -81,7 +81,7 @@ public void testToStringOct() throws Exception {
ObjectReference or = this.getObjectReference("Foo");
Map<String, Object> options = formatter.getDefaultOptions();
options.put(NUMERIC_FORMAT_OPTION, NumericFormatEnum.OCT);
assertEquals("Failed to format an object.", String.format("MockType (id=%#o)", or.uniqueID()),
assertEquals("Failed to format an object.", String.format("MockType@%#o", or.uniqueID()),
formatter.toString(or, options));
}

Expand Down
Loading
0