From b3687604d27b241586dbeb79ffb737c584e814f7 Mon Sep 17 00:00:00 2001 From: Daniel Pfeffer Date: Tue, 14 Apr 2026 16:30:53 +0200 Subject: [PATCH 1/2] added attributes to VariablePresentationHint as specified in protocol definition --- .../handler/VariablesRequestHandler.java | 30 +++++++++++++++++-- .../java/debug/core/protocol/Types.java | 24 +++++++-------- 2 files changed, 37 insertions(+), 17 deletions(-) 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..dc0ba1f63 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 @@ -82,7 +82,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 +370,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 +393,28 @@ 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 fields are compile-time constants in Java + if (variable.field.isStatic()) { + 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; } } From 45ef1b874efa89453927d5f9f9feb127eb96f01f Mon Sep 17 00:00:00 2001 From: Daniel Pfeffer Date: Wed, 15 Apr 2026 13:31:11 +0200 Subject: [PATCH 2/2] Fixed issue with lazy VariablePresentationHint. Restricted constant to Primitive & StringRefs --- .../adapter/handler/VariablesRequestHandler.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) 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 dc0ba1f63..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); @@ -375,7 +377,7 @@ public CompletableFuture handle(Command command, Arguments arguments, Types.Variable typedVariables = new Types.Variable(name, valueString, typeString, referenceId, evaluateName); typedVariables.indexedVariables = Math.max(indexedVariables, 0); if ((varProxy != null && varProxy.isLazyVariable()) || (rawAttributes.length > 0)) { - typedVariables.presentationHint = new VariablePresentationHint(varProxy == null || varProxy.isLazyVariable(), rawAttributes); + typedVariables.presentationHint = new VariablePresentationHint(varProxy != null && varProxy.isLazyVariable(), rawAttributes); } if (detailsValue != null) { @@ -400,14 +402,22 @@ private String[] extractAttributes(Variable variable) { attributes.add("static"); } if (variable.field.isFinal()) { - // static final fields are compile-time constants in Java - if (variable.field.isStatic()) { + // 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"); }