Skip to content

Commit

Permalink
Fix variable perf issue & logical structure doesn't work with JDK 11 (#…
Browse files Browse the repository at this point in the history
…268)

* Fix variable perf issue & logical structure doesn't work with JDK 11

Signed-off-by: Jinbo Wang <[email protected]>

* Address review comments

Signed-off-by: Jinbo Wang <[email protected]>

* Refactor the logical structure evaluation code

Signed-off-by: Jinbo Wang <[email protected]>
  • Loading branch information
testforstephen authored Apr 17, 2019
1 parent 81bdeb2 commit f921a67
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@
import java.util.logging.Logger;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;

import com.microsoft.java.debug.core.Configuration;
import com.microsoft.java.debug.core.DebugSettings;
import com.microsoft.java.debug.core.adapter.AdapterUtils;
Expand All @@ -37,6 +35,7 @@
import com.microsoft.java.debug.core.adapter.IStackFrameManager;
import com.microsoft.java.debug.core.adapter.variables.IVariableFormatter;
import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure;
import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure.LogicalStructureExpression;
import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure.LogicalVariable;
import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructureManager;
import com.microsoft.java.debug.core.adapter.variables.StackFrameReference;
Expand All @@ -51,9 +50,13 @@
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;
Expand Down Expand Up @@ -121,35 +124,38 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
} else {
try {
ObjectReference containerObj = (ObjectReference) containerNode.getProxiedVariable();
if (DebugSettings.getCurrent().showLogicalStructure) {
IEvaluationProvider evaluationEngine = context.getProvider(IEvaluationProvider.class);
if (DebugSettings.getCurrent().showLogicalStructure && evaluationEngine != null) {
JavaLogicalStructure logicalStructure = JavaLogicalStructureManager.getLogicalStructure(containerObj);
IEvaluationProvider evaluationEngine = context.getProvider(IEvaluationProvider.class);
if (logicalStructure != null && evaluationEngine != null) {
String expression = logicalStructure.getValue();
while (logicalStructure != null) {
LogicalStructureExpression valueExpression = logicalStructure.getValueExpression();
LogicalVariable[] logicalVariables = logicalStructure.getVariables();
try {
if (StringUtils.isNotEmpty(expression)) {
Value value = evaluationEngine.evaluate(expression, containerObj,
containerNode.getThread()).get();
if (valueExpression != null) {
Value value = logicalStructure.getValue(containerObj, containerNode.getThread(), evaluationEngine);
if (value instanceof ObjectReference) {
containerObj = (ObjectReference) value;
logicalStructure = JavaLogicalStructureManager.getLogicalStructure(containerObj);
continue;
} else {
childrenList = Arrays.asList(new Variable("logical structure", value));
}
} else if (logicalVariables != null && logicalVariables.length > 0) {
for (LogicalVariable logicalVariable : logicalVariables) {
String name = logicalVariable.getName();
Value value = evaluationEngine.evaluate(logicalVariable.getValue(), containerObj,
containerNode.getThread()).get();
Value value = logicalVariable.getValue(containerObj, containerNode.getThread(), evaluationEngine);
childrenList.add(new Variable(name, value));
}
}
} catch (InterruptedException | ExecutionException e) {
} catch (InterruptedException | ExecutionException | InvalidTypeException
| ClassNotLoadedException | IncompatibleThreadStateException | InvocationException 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()),
e);
}

logicalStructure = null;
}
}

Expand Down Expand Up @@ -211,24 +217,22 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
int indexedVariables = -1;
if (value instanceof ArrayReference) {
indexedVariables = ((ArrayReference) value).length();
} else if (DebugSettings.getCurrent().showLogicalStructure
&& value instanceof ObjectReference
} else if (value instanceof ObjectReference && DebugSettings.getCurrent().showLogicalStructure
&& context.getProvider(IEvaluationProvider.class) != null
&& JavaLogicalStructureManager.isIndexedVariable((ObjectReference) value)) {
String logicalSizeExpression = JavaLogicalStructureManager.getLogicalSize((ObjectReference) value);
IEvaluationProvider evaluationEngine = context.getProvider(IEvaluationProvider.class);
if (StringUtils.isNotBlank(logicalSizeExpression) && evaluationEngine != null) {
try {
Value size = evaluationEngine.evaluate(logicalSizeExpression, (ObjectReference) value,
containerNode.getThread()).get();
if (size instanceof IntegerValue) {
indexedVariables = ((IntegerValue) size).value();
}
} catch (InterruptedException | ExecutionException e) {
logger.log(Level.INFO,
String.format("Failed to get the logical size for the type %s.", value.type().name()), e);
try {
Value sizeValue = JavaLogicalStructureManager.getLogicalSize((ObjectReference) value, containerNode.getThread(), evaluationEngine);
if (sizeValue != null && sizeValue instanceof IntegerValue) {
indexedVariables = ((IntegerValue) sizeValue).value();
}
} catch (InvalidTypeException | ClassNotLoadedException | IncompatibleThreadStateException
| InvocationException | InterruptedException | ExecutionException | UnsupportedOperationException e) {
logger.log(Level.INFO,
String.format("Failed to get the logical size for the type %s.", value.type().name()), e);
}
}

int referenceId = 0;
if (indexedVariables > 0 || (indexedVariables < 0 && VariableUtils.hasChildren(value, showStaticVariables))) {
VariableProxy varProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,52 @@

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

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;

import org.apache.commons.lang3.StringUtils;

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;
import com.sun.jdi.Value;

public class JavaLogicalStructure {
private final String type;
private final String value;
private final String size;
private final LogicalStructureExpression valueExpression;
private final LogicalStructureExpression sizeExpression;
private final LogicalVariable[] variables;

/**
* Constructor.
*/
public JavaLogicalStructure(String type, String value, String size, LogicalVariable[] variables) {
this.value = value;
public JavaLogicalStructure(String type, LogicalStructureExpression valueExpression, LogicalStructureExpression sizeExpression,
LogicalVariable[] variables) {
this.valueExpression = valueExpression;
this.type = type;
this.size = size;
this.sizeExpression = sizeExpression;
this.variables = variables;
}

public String getValue() {
return value;
}

public String getType() {
return type;
}

public String getSize() {
return size;
public LogicalStructureExpression getValueExpression() {
return valueExpression;
}

public LogicalStructureExpression getSizeExpression() {
return sizeExpression;
}

public LogicalVariable[] getVariables() {
Expand Down Expand Up @@ -81,25 +91,110 @@ public boolean providesLogicalStructure(ObjectReference obj) {
return false;
}

public boolean isIndexedVariable() {
return StringUtils.isNotBlank(size);
/**
* 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 {
if (sizeExpression == null) {
throw new UnsupportedOperationException("The object hasn't defined the logical size operation.");
}

return getValue(thisObject, sizeExpression, thread, evaluationEngine);
}

/**
* Return the logical value of the specified thisObject.
*/
public Value getValue(ObjectReference thisObject, ThreadReference thread, IEvaluationProvider evaluationEngine)
throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException,
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 {
if (expression.type == LogicalStructureExpressionType.METHOD) {
return getValueByMethod(thisObject, expression.value, thread);
} else if (expression.type == LogicalStructureExpressionType.FIELD) {
return getValueByField(thisObject, expression.value, 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<Method> methods = thisObject.referenceType().allMethods();
Method targetMethod = null;
for (Method method : methods) {
if (Objects.equals(method.name(), methodName) && method.argumentTypeNames().isEmpty()) {
targetMethod = method;
break;
}
}

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<Field> fields = thisObject.referenceType().allFields();
Field targetField = null;
for (Field field : fields) {
if (Objects.equals(field.name(), filedName)) {
targetField = field;
break;
}
}

if (targetField == null) {
return null;
}

return thisObject.getValue(targetField);
}

public static class LogicalVariable {
private final String name;
private final String value;
private final LogicalStructureExpression valueExpression;

public LogicalVariable(String name, String value) {
public LogicalVariable(String name, LogicalStructureExpression valueExpression) {
this.name = name;
this.value = value;
this.valueExpression = valueExpression;
}

public String getName() {
return name;
}

public String getValue() {
return value;
public Value getValue(ObjectReference thisObject, ThreadReference thread, IEvaluationProvider evaluationEngine)
throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException,
InterruptedException, ExecutionException {
return JavaLogicalStructure.getValue(thisObject, valueExpression, thread, evaluationEngine);
}
}

public static class LogicalStructureExpression {
public LogicalStructureExpressionType type;
public String value;

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

public static enum LogicalStructureExpressionType {
FIELD, METHOD, EVALUATION_SNIPPET
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,36 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
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;

public class JavaLogicalStructureManager {
private static final List<JavaLogicalStructure> supportedLogicalStructures = Collections.synchronizedList(new ArrayList<>());

static {
supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Map", "return entrySet().toArray();", "return size();", new LogicalVariable[0]));
supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Map",
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, "entrySet"),
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, "size"),
new LogicalVariable[0]));
supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Map$Entry", null, null, new LogicalVariable[] {
new LogicalVariable("key", "return getKey();"),
new LogicalVariable("value", "return getValue();")
new LogicalVariable("key", new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, "getKey")),
new LogicalVariable("value", new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, "getValue"))
}));
supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Collection", "return toArray();", "return size();", new LogicalVariable[0]));
supportedLogicalStructures.add(new JavaLogicalStructure("java.util.Collection",
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, "toArray"),
new LogicalStructureExpression(LogicalStructureExpressionType.METHOD, "size"),
new LogicalVariable[0]));
}

/**
Expand All @@ -43,13 +59,25 @@ public static JavaLogicalStructure getLogicalStructure(ObjectReference obj) {
return null;
}

/**
* Return true if the specified Object has defined the logical size.
*/
public static boolean isIndexedVariable(ObjectReference obj) {
JavaLogicalStructure structure = getLogicalStructure(obj);
return structure != null && structure.isIndexedVariable();
return structure != null && structure.getSizeExpression() != null;
}

public static String getLogicalSize(ObjectReference obj) {
JavaLogicalStructure structure = getLogicalStructure(obj);
return structure == null ? null : structure.getSize();
/**
* 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 {
JavaLogicalStructure structure = getLogicalStructure(thisObject);
if (structure == null) {
return null;
}

return structure.getSize(thisObject, thread, evaluationEngine);
}
}

0 comments on commit f921a67

Please sign in to comment.