Skip to content

Commit

Permalink
Support lazy loading object values from toString() (#401)
Browse files Browse the repository at this point in the history
  • Loading branch information
CsCherrYY authored Mar 24, 2022
1 parent 798d259 commit f704199
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import com.microsoft.java.debug.core.protocol.Requests.Arguments;
import com.microsoft.java.debug.core.protocol.Requests.Command;
import com.microsoft.java.debug.core.protocol.Requests.VariablesArguments;
import com.microsoft.java.debug.core.protocol.Types.VariablePresentationHint;
import com.microsoft.java.debug.core.protocol.Responses;
import com.microsoft.java.debug.core.protocol.Types;
import com.sun.jdi.AbsentInformationException;
Expand Down Expand Up @@ -94,6 +95,15 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
}

VariableProxy containerNode = (VariableProxy) container;

if (containerNode.isLazyVariable() && DebugSettings.getCurrent().showToString) {
Types.Variable typedVariable = this.resolveLazyVariable(context, containerNode, variableFormatter, options, evaluationEngine);
if (typedVariable != null) {
list.add(typedVariable);
response.body = new Responses.VariablesResponseBody(list);
return CompletableFuture.completedFuture(response);
}
}
List<Variable> childrenList = new ArrayList<>();
IStackFrameManager stackFrameManager = context.getStackFrameManager();
String containerEvaluateName = containerNode.getEvaluateName();
Expand Down Expand Up @@ -266,10 +276,9 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
}
}

int referenceId = 0;
VariableProxy varProxy = null;
if (indexedVariables > 0 || (indexedVariables < 0 && value instanceof ObjectReference)) {
VariableProxy varProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), value, containerNode, evaluateName);
referenceId = context.getRecyclableIdPool().addObject(containerNode.getThreadId(), varProxy);
varProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), value, containerNode, evaluateName);
varProxy.setIndexedVariable(indexedVariables >= 0);
varProxy.setUnboundedType(javaVariable.isUnboundedType());
}
Expand All @@ -296,26 +305,38 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
typeString = "";
}

Types.Variable typedVariables = new Types.Variable(name, valueString, typeString, referenceId, evaluateName);
typedVariables.indexedVariables = Math.max(indexedVariables, 0);

String detailsValue = null;
if (hasErrors) {
// If failed to resolve the variable value, skip the details info as well.
} else if (sizeValue != null) {
detailsValue = "size=" + variableFormatter.valueToString(sizeValue, options);
} else if (DebugSettings.getCurrent().showToString) {
try {
detailsValue = VariableDetailUtils.formatDetailsValue(value, containerNode.getThread(), variableFormatter, options, evaluationEngine);
} catch (OutOfMemoryError e) {
logger.log(Level.SEVERE, "Failed to compute the toString() value of a large object", e);
detailsValue = "<Unable to display the details of a large object>";
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to compute the toString() value", e);
detailsValue = "<Failed to resolve the variable details due to \"" + e.getMessage() + "\">";
if (VariableDetailUtils.isLazyLoadingSupported(value) && varProxy != null) {
varProxy.setLazyVariable(true);
} else {
try {
detailsValue = VariableDetailUtils.formatDetailsValue(value, containerNode.getThread(), variableFormatter, options, evaluationEngine);
} catch (OutOfMemoryError e) {
logger.log(Level.SEVERE, "Failed to compute the toString() value of a large object", e);
detailsValue = "<Unable to display the details of a large object>";
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to compute the toString() value", e);
detailsValue = "<Failed to resolve the variable details due to \"" + e.getMessage() + "\">";
}
}
}

int referenceId = 0;
if (varProxy != null) {
referenceId = context.getRecyclableIdPool().addObject(containerNode.getThreadId(), varProxy);
}

Types.Variable typedVariables = new Types.Variable(name, valueString, typeString, referenceId, evaluateName);
typedVariables.indexedVariables = Math.max(indexedVariables, 0);
if (varProxy != null && varProxy.isLazyVariable()) {
typedVariables.presentationHint = new VariablePresentationHint(true);
}

if (detailsValue != null) {
typedVariables.value = typedVariables.value + " " + detailsValue;
}
Expand All @@ -331,6 +352,25 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
return CompletableFuture.completedFuture(response);
}

private Types.Variable resolveLazyVariable(IDebugAdapterContext context, VariableProxy containerNode, IVariableFormatter variableFormatter,
Map<String, Object> options, IEvaluationProvider evaluationEngine) {
VariableProxy valueReferenceProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(),
containerNode.getProxiedVariable(), null /** container */, containerNode.getEvaluateName());
valueReferenceProxy.setIndexedVariable(containerNode.isIndexedVariable());
valueReferenceProxy.setUnboundedType(containerNode.isUnboundedType());
int referenceId = context.getRecyclableIdPool().addObject(containerNode.getThreadId(), valueReferenceProxy);
// this proxiedVariable is intermediate object, see https:/microsoft/vscode/issues/135147#issuecomment-1076240074
Object proxiedVariable = containerNode.getProxiedVariable();
if (proxiedVariable instanceof ObjectReference) {
ObjectReference variable = (ObjectReference) proxiedVariable;
String valueString = variableFormatter.valueToString(variable, options);
String detailString = VariableDetailUtils.formatDetailsValue(variable, containerNode.getThread(), variableFormatter, options,
evaluationEngine);
return new Types.Variable("", valueString + " " + detailString, "", referenceId, containerNode.getEvaluateName());
}
return null;
}

private Set<String> getDuplicateNames(Collection<String> list) {
Set<String> result = new HashSet<>();
Set<String> set = new HashSet<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,18 @@ private static boolean isClassType(Value value, String typeName) {

return Objects.equals(((ObjectReference) value).type().name(), typeName);
}

public static boolean isLazyLoadingSupported(Value value) {
if (isClassType(value, STRING_TYPE)) {
return false;
}
if (!(value instanceof ObjectReference)) {
return false;
}
String inheritedType = findInheritedType(value, COLLECTION_TYPES);
if (inheritedType == null && !containsToStringMethod((ObjectReference) value)) {
return false;
}
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class VariableProxy {
private final String evaluateName;
private boolean isIndexedVariable;
private boolean isUnboundedType = false;
private boolean isLazyVariable = false;

/**
* Create a variable reference.
Expand Down Expand Up @@ -75,7 +76,8 @@ public boolean equals(Object obj) {
}
VariableProxy other = (VariableProxy) obj;
return Objects.equals(scopeName, other.scopeName) && Objects.equals(getThreadId(), other.getThreadId())
&& Objects.equals(variable, other.variable) && Objects.equals(evaluateName, other.evaluateName);
&& Objects.equals(variable, other.variable) && Objects.equals(evaluateName, other.evaluateName)
&& Objects.equals(isLazyVariable, other.isLazyVariable);
}

public long getThreadId() {
Expand Down Expand Up @@ -109,4 +111,13 @@ public boolean isUnboundedType() {
public void setUnboundedType(boolean isUnboundedType) {
this.isUnboundedType = isUnboundedType;
}

public boolean isLazyVariable() {
return isLazyVariable;
}

public void setLazyVariable(boolean isLazyVariable) {
this.isLazyVariable = isLazyVariable;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public static class Variable {
public int namedVariables;
public int indexedVariables;
public String evaluateName;
public VariablePresentationHint presentationHint;

/**
* Constructor.
Expand Down Expand Up @@ -343,6 +344,14 @@ public static class ExceptionDetails {
public ExceptionDetails[] innerException;
}

public static class VariablePresentationHint {
public boolean lazy;

public VariablePresentationHint(boolean lazy) {
this.lazy = lazy;
}
}

public static class Capabilities {
public boolean supportsConfigurationDoneRequest;
public boolean supportsHitConditionalBreakpoints;
Expand Down

0 comments on commit f704199

Please sign in to comment.