diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java index 11a6391de..47854528f 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java @@ -65,6 +65,8 @@ import com.sun.jdi.StringReference; import com.sun.jdi.Type; import com.sun.jdi.Value; +import com.sun.jdi.PrimitiveType; +import com.sun.jdi.ClassNotLoadedException; public class VariablesRequestHandler implements IDebugRequestHandler { protected static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); @@ -82,7 +84,7 @@ public class VariablesRequestHandler implements IDebugRequestHandler { * single-threaded JDWP request processing strategy, * a single JDWP latency is about 10ms. */ - static final long USABLE_JDWP_LATENCY = 10/**ms*/; + static final long USABLE_JDWP_LATENCY = 10/*ms*/; @Override public List getTargetCommands() { @@ -370,10 +372,12 @@ public CompletableFuture handle(Command command, Arguments arguments, referenceId = context.getRecyclableIdPool().addObject(containerNode.getThreadId(), varProxy); } + String[] rawAttributes = extractAttributes(javaVariable); + 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 ((varProxy != null && varProxy.isLazyVariable()) || (rawAttributes.length > 0)) { + typedVariables.presentationHint = new VariablePresentationHint(varProxy != null && varProxy.isLazyVariable(), rawAttributes); } if (detailsValue != null) { @@ -391,6 +395,36 @@ public CompletableFuture handle(Command command, Arguments arguments, return CompletableFuture.completedFuture(response); } + private String[] extractAttributes(Variable variable) { + final List attributes = new ArrayList<>(); + if (variable.field != null) { + if (variable.field.isStatic()) { + attributes.add("static"); + } + if (variable.field.isFinal()) { + // static final Primitive or StringRef fields are compile-time constants in Java + boolean isPrimitive; + try { + isPrimitive = variable.field.type() instanceof PrimitiveType; + } catch (ClassNotLoadedException e) { /* type() not yet loaded indicates some ReferenceType */ + isPrimitive = false; + } + boolean canBeTrueConstant = (isPrimitive || variable.field instanceof StringReference); + if (variable.field.isStatic() && canBeTrueConstant) { + attributes.add("constant"); + } + attributes.add("readOnly"); + } + } + // local 'final' variables get inlined by the compiler and do not appear + + if (variable.value instanceof StringReference) { + attributes.add("rawString"); + } + + return attributes.toArray(new String[0]); + } + private boolean supportsLogicStructureView(IDebugAdapterContext context) { return (!useAsyncJDWP(context) || context.getJDWPLatency() <= USABLE_JDWP_LATENCY) && DebugSettings.getCurrent().showLogicalStructure; diff --git a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java index 33308af6d..9d32d4783 100644 --- a/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java +++ b/com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/protocol/Types.java @@ -49,19 +49,13 @@ public static class StackFrame { /** * Constructs a StackFrame with the given information. * - * @param id - * the stack frame id - * @param name - * the stack frame name - * @param src - * source info of the stack frame - * @param ln - * line number of the stack frame - * @param col - * column number of the stack frame - * @param presentationHint - * An optional hint for how to present this frame in the UI. - * Values: 'normal', 'label', 'subtle' + * @param id the stack frame id + * @param name the stack frame name + * @param src source info of the stack frame + * @param ln line number of the stack frame + * @param col column number of the stack frame + * @param presentationHint An optional hint for how to present this frame in the UI. + * Values: 'normal', 'label', 'subtle' */ public StackFrame(int id, String name, Source src, int ln, int col, String presentationHint) { this.id = id; @@ -408,9 +402,11 @@ public static class ExceptionDetails { public static class VariablePresentationHint { public boolean lazy; + public String[] attributes; - public VariablePresentationHint(boolean lazy) { + public VariablePresentationHint(boolean lazy, String[] attributes) { this.lazy = lazy; + this.attributes = attributes; } }