Skip to content

Commit 6a9e196

Browse files
Show logical structure for Collection and Map instances (#262)
* Show logical structure for Collection and Map instances Signed-off-by: Jinbo Wang <[email protected]> * Fix the case of empty Map Signed-off-by: Jinbo Wang <[email protected]> * Address review comments Signed-off-by: Jinbo Wang <[email protected]> * Fix the performance issue when showing the logical structure for huge Map and List Signed-off-by: Jinbo Wang <[email protected]>
1 parent 05e40f9 commit 6a9e196

File tree

9 files changed

+316
-37
lines changed

9 files changed

+316
-37
lines changed

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2017 Microsoft Corporation and others.
2+
* Copyright (c) 2017-2019 Microsoft Corporation and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -25,6 +25,7 @@ public final class DebugSettings {
2525
public boolean showQualifiedNames = false;
2626
public boolean showHex = false;
2727
public boolean enableHotCodeReplace = false;
28+
public boolean showLogicalStructure = true;
2829
public String logLevel;
2930
public String javaHome;
3031

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2017 Microsoft Corporation and others.
2+
* Copyright (c) 2017-2019 Microsoft Corporation and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -14,6 +14,7 @@
1414
import java.util.concurrent.CompletableFuture;
1515

1616
import com.microsoft.java.debug.core.IEvaluatableBreakpoint;
17+
import com.sun.jdi.ObjectReference;
1718
import com.sun.jdi.ThreadReference;
1819
import com.sun.jdi.Value;
1920

@@ -31,16 +32,26 @@ public interface IEvaluationProvider extends IProvider {
3132
boolean isInEvaluation(ThreadReference thread);
3233

3334
/**
34-
* Evaluate the expression at the given thread and stack frame depth, return the promise which is to be resolved/rejected when
35+
* Evaluate the expression in the context of the specified stack frame, return the promise which is to be resolved/rejected when
3536
* the evaluation finishes.
3637
*
3738
* @param expression The expression to be evaluated
38-
* @param thread The jdi thread to the expression will be executed at
39-
* @param depth The depth of stackframe of the stopped thread
39+
* @param thread The suspended thread the evaluation will be executed at
40+
* @param depth The stack frame depth in the suspended thread
4041
* @return the evaluation result future
4142
*/
4243
CompletableFuture<Value> evaluate(String expression, ThreadReference thread, int depth);
4344

45+
/**
46+
* Evaluate the expression in the context of the specified 'this' object, return the promise which is to be resolved/rejected when
47+
* the evaluation finishes.
48+
* @param expression The expression to be evaluated
49+
* @param thisContext The 'this' context for the evaluation
50+
* @param thread The suspended thread which the evaluation will be executed at
51+
* @return the evaluation result future
52+
*/
53+
CompletableFuture<Value> evaluate(String expression, ObjectReference thisContext, ThreadReference thread);
54+
4455
/**
4556
* Evaluate the conditional breakpoint or logpoint at the given thread and return the promise which is to be resolved/rejected when
4657
* the evaluation finishes.

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

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2017 Microsoft Corporation and others.
2+
* Copyright (c) 2017-2019 Microsoft Corporation and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -20,15 +20,22 @@
2020
import java.util.Map;
2121
import java.util.Set;
2222
import java.util.concurrent.CompletableFuture;
23+
import java.util.concurrent.ExecutionException;
2324
import java.util.stream.Collectors;
2425

26+
import org.apache.commons.lang3.StringUtils;
27+
2528
import com.microsoft.java.debug.core.DebugSettings;
2629
import com.microsoft.java.debug.core.adapter.AdapterUtils;
2730
import com.microsoft.java.debug.core.adapter.ErrorCode;
2831
import com.microsoft.java.debug.core.adapter.IDebugAdapterContext;
2932
import com.microsoft.java.debug.core.adapter.IDebugRequestHandler;
33+
import com.microsoft.java.debug.core.adapter.IEvaluationProvider;
3034
import com.microsoft.java.debug.core.adapter.IStackFrameManager;
3135
import com.microsoft.java.debug.core.adapter.variables.IVariableFormatter;
36+
import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure;
37+
import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure.LogicalVariable;
38+
import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructureManager;
3239
import com.microsoft.java.debug.core.adapter.variables.StackFrameReference;
3340
import com.microsoft.java.debug.core.adapter.variables.Variable;
3441
import com.microsoft.java.debug.core.adapter.variables.VariableProxy;
@@ -41,6 +48,7 @@
4148
import com.microsoft.java.debug.core.protocol.Types;
4249
import com.sun.jdi.AbsentInformationException;
4350
import com.sun.jdi.ArrayReference;
51+
import com.sun.jdi.IntegerValue;
4452
import com.sun.jdi.InternalException;
4553
import com.sun.jdi.InvalidStackFrameException;
4654
import com.sun.jdi.ObjectReference;
@@ -81,7 +89,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
8189
}
8290

8391
VariableProxy containerNode = (VariableProxy) container;
84-
List<Variable> childrenList;
92+
List<Variable> childrenList = new ArrayList<>();
8593
IStackFrameManager stackFrameManager = context.getStackFrameManager();
8694
if (containerNode.getProxiedVariable() instanceof StackFrameReference) {
8795
StackFrameReference stackFrameReference = (StackFrameReference) containerNode.getProxiedVariable();
@@ -109,10 +117,41 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
109117
} else {
110118
try {
111119
ObjectReference containerObj = (ObjectReference) containerNode.getProxiedVariable();
112-
if (varArgs.count > 0) {
113-
childrenList = VariableUtils.listFieldVariables(containerObj, varArgs.start, varArgs.count);
114-
} else {
115-
childrenList = VariableUtils.listFieldVariables(containerObj, showStaticVariables);
120+
if (DebugSettings.getCurrent().showLogicalStructure) {
121+
JavaLogicalStructure logicalStructure = JavaLogicalStructureManager.getLogicalStructure(containerObj);
122+
IEvaluationProvider evaluationEngine = context.getProvider(IEvaluationProvider.class);
123+
if (logicalStructure != null && evaluationEngine != null) {
124+
String expression = logicalStructure.getValue();
125+
LogicalVariable[] logicalVariables = logicalStructure.getVariables();
126+
try {
127+
if (StringUtils.isNotEmpty(expression)) {
128+
Value value = evaluationEngine.evaluate(expression, containerObj,
129+
containerNode.getThread()).get();
130+
if (value instanceof ObjectReference) {
131+
containerObj = (ObjectReference) value;
132+
} else {
133+
childrenList = Arrays.asList(new Variable("logical structure", value));
134+
}
135+
} else if (logicalVariables != null && logicalVariables.length > 0) {
136+
for (LogicalVariable logicalVariable : logicalVariables) {
137+
String name = logicalVariable.getName();
138+
Value value = evaluationEngine.evaluate(logicalVariable.getValue(), containerObj,
139+
containerNode.getThread()).get();
140+
childrenList.add(new Variable(name, value));
141+
}
142+
}
143+
} catch (InterruptedException | ExecutionException e) {
144+
// do nothing.
145+
}
146+
}
147+
}
148+
149+
if (childrenList.isEmpty() && VariableUtils.hasChildren(containerObj, showStaticVariables)) {
150+
if (varArgs.count > 0) {
151+
childrenList = VariableUtils.listFieldVariables(containerObj, varArgs.start, varArgs.count);
152+
} else {
153+
childrenList = VariableUtils.listFieldVariables(containerObj, showStaticVariables);
154+
}
116155
}
117156
} catch (AbsentInformationException e) {
118157
throw AdapterUtils.createCompletionException(
@@ -162,17 +201,35 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
162201
if (variableNameMap.containsKey(javaVariable)) {
163202
name = variableNameMap.get(javaVariable);
164203
}
204+
int indexedVariables = -1;
205+
if (value instanceof ArrayReference) {
206+
indexedVariables = ((ArrayReference) value).length();
207+
} else if (DebugSettings.getCurrent().showLogicalStructure
208+
&& value instanceof ObjectReference
209+
&& JavaLogicalStructureManager.isIndexedVariable((ObjectReference) value)) {
210+
String logicalSizeExpression = JavaLogicalStructureManager.getLogicalSize((ObjectReference) value);
211+
IEvaluationProvider evaluationEngine = context.getProvider(IEvaluationProvider.class);
212+
if (StringUtils.isNotBlank(logicalSizeExpression) && evaluationEngine != null) {
213+
try {
214+
Value size = evaluationEngine.evaluate(logicalSizeExpression, (ObjectReference) value,
215+
containerNode.getThread()).get();
216+
if (size instanceof IntegerValue) {
217+
indexedVariables = ((IntegerValue) size).value();
218+
}
219+
} catch (InterruptedException | ExecutionException e) {
220+
// do nothing.
221+
}
222+
}
223+
}
165224
int referenceId = 0;
166-
if (value instanceof ObjectReference && VariableUtils.hasChildren(value, showStaticVariables)) {
225+
if (indexedVariables > 0 || (indexedVariables < 0 && VariableUtils.hasChildren(value, showStaticVariables))) {
167226
VariableProxy varProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), value);
168227
referenceId = context.getRecyclableIdPool().addObject(containerNode.getThreadId(), varProxy);
169228
}
170229
Types.Variable typedVariables = new Types.Variable(name, variableFormatter.valueToString(value, options),
171230
variableFormatter.typeToString(value == null ? null : value.type(), options),
172231
referenceId, null);
173-
if (javaVariable.value instanceof ArrayReference) {
174-
typedVariables.indexedVariables = ((ArrayReference) javaVariable.value).length();
175-
}
232+
typedVariables.indexedVariables = Math.max(indexedVariables, 0);
176233
list.add(typedVariables);
177234
}
178235
response.body = new Responses.VariablesResponseBody(list);
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2019 Microsoft Corporation and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Microsoft Corporation - initial API and implementation
10+
*******************************************************************************/
11+
12+
package com.microsoft.java.debug.core.adapter.variables;
13+
14+
import java.util.List;
15+
import java.util.Objects;
16+
17+
import org.apache.commons.lang3.StringUtils;
18+
19+
import com.sun.jdi.ClassType;
20+
import com.sun.jdi.InterfaceType;
21+
import com.sun.jdi.ObjectReference;
22+
import com.sun.jdi.Type;
23+
24+
public class JavaLogicalStructure {
25+
private final String type;
26+
private final String value;
27+
private final String size;
28+
private final LogicalVariable[] variables;
29+
30+
/**
31+
* Constructor.
32+
*/
33+
public JavaLogicalStructure(String type, String value, String size, LogicalVariable[] variables) {
34+
this.value = value;
35+
this.type = type;
36+
this.size = size;
37+
this.variables = variables;
38+
}
39+
40+
public String getValue() {
41+
return value;
42+
}
43+
44+
public String getType() {
45+
return type;
46+
}
47+
48+
public String getSize() {
49+
return size;
50+
}
51+
52+
public LogicalVariable[] getVariables() {
53+
return variables;
54+
}
55+
56+
/**
57+
* Returns whether to support the logical structure view for the given object instance.
58+
*/
59+
public boolean providesLogicalStructure(ObjectReference obj) {
60+
Type variableType = obj.type();
61+
if (!(variableType instanceof ClassType)) {
62+
return false;
63+
}
64+
65+
ClassType classType = (ClassType) variableType;
66+
while (classType != null) {
67+
if (Objects.equals(type, classType.name())) {
68+
return true;
69+
}
70+
71+
classType = classType.superclass();
72+
}
73+
74+
List<InterfaceType> interfaceTypes = ((ClassType) variableType).allInterfaces();
75+
for (InterfaceType interfaceType : interfaceTypes) {
76+
if (Objects.equals(type, interfaceType.name())) {
77+
return true;
78+
}
79+
}
80+
81+
return false;
82+
}
83+
84+
public boolean isIndexedVariable() {
85+
return StringUtils.isNotBlank(size);
86+
}
87+
88+
public static class LogicalVariable {
89+
private final String name;
90+
private final String value;
91+
92+
public LogicalVariable(String name, String value) {
93+
this.name = name;
94+
this.value = value;
95+
}
96+
97+
public String getName() {
98+
return name;
99+
}
100+
101+
public String getValue() {
102+
return value;
103+
}
104+
}
105+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2019 Microsoft Corporation and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Microsoft Corporation - initial API and implementation
10+
*******************************************************************************/
11+
12+
package com.microsoft.java.debug.core.adapter.variables;
13+
14+
import java.util.ArrayList;
15+
import java.util.Collections;
16+
import java.util.List;
17+
18+
import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure.LogicalVariable;
19+
import com.sun.jdi.ObjectReference;
20+
21+
public class JavaLogicalStructureManager {
22+
private static final List<JavaLogicalStructure> supportedLogicalStructures = Collections.synchronizedList(new ArrayList<>());
23+
24+
static {
25+
supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Map", "return entrySet().toArray();", "return size();", new LogicalVariable[0]));
26+
supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Map$Entry", null, null, new LogicalVariable[] {
27+
new LogicalVariable("key", "return getKey();"),
28+
new LogicalVariable("value", "return getValue();")
29+
}));
30+
supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Collection", "return toArray();", "return size();", new LogicalVariable[0]));
31+
}
32+
33+
/**
34+
* Return the provided logical structure handler for the given variable.
35+
*/
36+
public static JavaLogicalStructure getLogicalStructure(ObjectReference obj) {
37+
for (JavaLogicalStructure structure : supportedLogicalStructures) {
38+
if (structure.providesLogicalStructure(obj)) {
39+
return structure;
40+
}
41+
}
42+
43+
return null;
44+
}
45+
46+
public static boolean isIndexedVariable(ObjectReference obj) {
47+
JavaLogicalStructure structure = getLogicalStructure(obj);
48+
return structure != null && structure.isIndexedVariable();
49+
}
50+
51+
public static String getLogicalSize(ObjectReference obj) {
52+
JavaLogicalStructure structure = getLogicalStructure(obj);
53+
return structure == null ? null : structure.getSize();
54+
}
55+
}

0 commit comments

Comments
 (0)