diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java
index c66fdbb730..4bd8f2d611 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java
@@ -405,6 +405,7 @@ void assert15Project() {
cfgs.add(createLaunchConfiguration(jp, "a.b.c.bug329294WithGenerics"));
cfgs.add(createLaunchConfiguration(jp, "a.b.c.bug403028"));
cfgs.add(createLaunchConfiguration(jp, "a.b.c.bug484686"));
+ cfgs.add(createLaunchConfiguration(jp, "a.b.c.PrimitivesTest"));
cfgs.add(createLaunchConfiguration(jp, "a.b.c.GenericMethodEntryTest"));
cfgs.add(createLaunchConfiguration(jp, "org.eclipse.debug.tests.targets.HcrClass", true));
cfgs.add(createLaunchConfiguration(jp, "a.b.c.Bug570988"));
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/variables/DetailFormatterTests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/variables/DetailFormatterTests.java
index 0c685e5c07..b9c766474e 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/variables/DetailFormatterTests.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/variables/DetailFormatterTests.java
@@ -340,4 +340,131 @@ public void testHoverWithNoTypeArguments() throws Exception {
removeAllBreakpoints();
}
}
+
+ public void testFormatterForPrimitivesInt() throws Exception {
+ IJavaThread thread = null;
+ DetailFormatter formatter = null;
+ JavaDetailFormattersManager jdfm = JavaDetailFormattersManager.getDefault();
+ try {
+ String typename = "a.b.c.PrimitivesTest";
+ createLineBreakpoint(26, typename);
+ thread = launchToBreakpoint(typename);
+ assertNotNull("The program did not suspend", thread);
+ String snippet = "this + 12";
+ formatter = new DetailFormatter("int", snippet, true);
+ jdfm.setAssociatedDetailFormatter(formatter);
+ IJavaVariable var = thread.findVariable("x");
+ assertNotNull("the variable 'x' must exist in the frame", var);
+ jdfm.computeValueDetail((IJavaValue) var.getValue(), thread, fListener);
+ waitForListenerValue();
+ assertNotNull("The IValue of the detailComputed callback cannot be null", fListener.value);
+ assertEquals("24", fListener.result.toString());
+
+ } finally {
+ jdfm.removeAssociatedDetailFormatter(formatter);
+ terminateAndRemove(thread);
+ removeAllBreakpoints();
+ }
+ }
+
+ public void testFormatterForPrimitivesfloat() throws Exception {
+ IJavaThread thread = null;
+ DetailFormatter formatter = null;
+ JavaDetailFormattersManager jdfm = JavaDetailFormattersManager.getDefault();
+ try {
+ String typename = "a.b.c.PrimitivesTest";
+ createLineBreakpoint(26, typename);
+ thread = launchToBreakpoint(typename);
+ assertNotNull("The program did not suspend", thread);
+ String snippet = "this + 10";
+ formatter = new DetailFormatter("float", snippet, true);
+ jdfm.setAssociatedDetailFormatter(formatter);
+ IJavaVariable var = thread.findVariable("f");
+ assertNotNull("the variable 'f' must exist in the frame", var);
+ jdfm.computeValueDetail((IJavaValue) var.getValue(), thread, fListener);
+ waitForListenerValue();
+ assertNotNull("The IValue of the detailComputed callback cannot be null", fListener.value);
+ assertEquals("20.0", fListener.result.toString());
+ } finally {
+ jdfm.removeAssociatedDetailFormatter(formatter);
+ terminateAndRemove(thread);
+ removeAllBreakpoints();
+ }
+ }
+
+ public void testFormatterForPrimitivesIntArrays() throws Exception {
+ IJavaThread thread = null;
+ DetailFormatter formatter = null;
+ JavaDetailFormattersManager jdfm = JavaDetailFormattersManager.getDefault();
+ try {
+ String typename = "a.b.c.PrimitivesTest";
+ createLineBreakpoint(26, typename);
+ thread = launchToBreakpoint(typename);
+ assertNotNull("The program did not suspend", thread);
+ String snippet = "this[1]";
+ formatter = new DetailFormatter("int[]", snippet, true);
+ jdfm.setAssociatedDetailFormatter(formatter);
+ IJavaVariable var = thread.findVariable("arInt");
+ assertNotNull("the variable 'arInt' must exist in the frame", var);
+ jdfm.computeValueDetail((IJavaValue) var.getValue(), thread, fListener);
+ waitForListenerValue();
+ assertNotNull("The IValue of the detailComputed callback cannot be null", fListener.value);
+ assertEquals("2", fListener.result.toString());
+ } finally {
+ jdfm.removeAssociatedDetailFormatter(formatter);
+ terminateAndRemove(thread);
+ removeAllBreakpoints();
+ }
+ }
+
+ public void testFormatterForPrimitivesIntArraysMulti() throws Exception {
+ IJavaThread thread = null;
+ DetailFormatter formatter = null;
+ JavaDetailFormattersManager jdfm = JavaDetailFormattersManager.getDefault();
+ try {
+ String typename = "a.b.c.PrimitivesTest";
+ createLineBreakpoint(26, typename);
+ thread = launchToBreakpoint(typename);
+ assertNotNull("The program did not suspend", thread);
+ String snippet = "this[0][0]";
+ formatter = new DetailFormatter("int[][]", snippet, true);
+ jdfm.setAssociatedDetailFormatter(formatter);
+ IJavaVariable var = thread.findVariable("mul");
+ assertNotNull("the variable 'mul' must exist in the frame", var);
+ jdfm.computeValueDetail((IJavaValue) var.getValue(), thread, fListener);
+ waitForListenerValue();
+ assertNotNull("The IValue of the detailComputed callback cannot be null", fListener.value);
+ assertEquals("1", fListener.result.toString());
+ } finally {
+ jdfm.removeAssociatedDetailFormatter(formatter);
+ terminateAndRemove(thread);
+ removeAllBreakpoints();
+ }
+ }
+
+ public void testFormatterForPrimitivesCharArray() throws Exception {
+ IJavaThread thread = null;
+ DetailFormatter formatter = null;
+ JavaDetailFormattersManager jdfm = JavaDetailFormattersManager.getDefault();
+ try {
+ String typename = "a.b.c.PrimitivesTest";
+ createLineBreakpoint(26, typename);
+ thread = launchToBreakpoint(typename);
+ assertNotNull("The program did not suspend", thread);
+ String snippet = "new String(this)";
+ formatter = new DetailFormatter("char[]", snippet, true);
+ jdfm.setAssociatedDetailFormatter(formatter);
+ IJavaVariable var = thread.findVariable("aCh");
+ assertNotNull("the variable 'aCh' must exist in the frame", var);
+ jdfm.computeValueDetail((IJavaValue) var.getValue(), thread, fListener);
+ waitForListenerValue();
+ assertNotNull("The IValue of the detailComputed callback cannot be null", fListener.value);
+ assertEquals("ab", fListener.result.toString());
+ } finally {
+ jdfm.removeAssociatedDetailFormatter(formatter);
+ terminateAndRemove(thread);
+ removeAllBreakpoints();
+ }
+ }
+
}
diff --git a/org.eclipse.jdt.debug.tests/testsource-j2se-1.5/a/b/c/PrimitivesTest.java b/org.eclipse.jdt.debug.tests/testsource-j2se-1.5/a/b/c/PrimitivesTest.java
new file mode 100644
index 0000000000..fb32332b63
--- /dev/null
+++ b/org.eclipse.jdt.debug.tests/testsource-j2se-1.5/a/b/c/PrimitivesTest.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2025 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package a.b.c;
+
+public class PrimitivesTest {
+ public static void main(String[] args) {
+ Double xd = Double.valueOf(1);
+ int x = 12;
+ float f = 10;
+ int[] arInt = { 1, 2, 3 };
+ char[] aCh = { 'a', 'b' };
+ boolean b = false;
+ double[] d = { 1, 3, 4 };
+ int[][] mul = { { 1, 3, 4 }, { 1, 3, 4 } };
+ int p1 = 120;
+ }
+}
diff --git a/org.eclipse.jdt.debug.ui/plugin.xml b/org.eclipse.jdt.debug.ui/plugin.xml
index c68914fc4c..2a878ca57b 100644
--- a/org.eclipse.jdt.debug.ui/plugin.xml
+++ b/org.eclipse.jdt.debug.ui/plugin.xml
@@ -753,22 +753,12 @@
id="org.eclipse.jdt.debug.ui.FilteredJavaVariableActions">
-
-
-
-
-
-
-
-
-
-
-
0) {
+ String s = (String) types[0];
+ fTypeNameText.setText(s);
+ fTypeSearched = true;
+ }
+ }
+
+ /**
+ * Returns a List of available primitives and arrays
+ *
+ * @return List primitives and arrays
+ */
+ @SuppressWarnings("nls")
+ private List getPrimitiveTypes() {
+ return List.of("int", "long", "short", "float", "double", "boolean", "char", "byte", "int[]", "long[]", "short[]", "float[]", "double[]", "boolean[]", "char[]", "byte[]");
+ }
+
/**
* Use the Java search engine to find the type which corresponds
* to the given name.
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDetailFormattersManager.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDetailFormattersManager.java
index b7c54d7f72..29282f3706 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDetailFormattersManager.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDetailFormattersManager.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2025 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -57,6 +57,7 @@
import org.eclipse.jdt.internal.debug.core.logicalstructures.JDIAllInstancesValue;
import org.eclipse.jdt.internal.debug.core.model.JDINullValue;
import org.eclipse.jdt.internal.debug.core.model.JDIReferenceListValue;
+import org.eclipse.jdt.internal.debug.core.model.JDIType;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.util.Util;
@@ -143,7 +144,7 @@ public void run() {
}
private void resolveFormatter(final IJavaValue value, final IJavaThread thread, final IValueDetailListener listener) {
- EvaluationListener evaluationListener= new EvaluationListener(value, thread, listener);
+ EvaluationListener evaluationListener = new EvaluationListener(value, thread, listener);
if (value instanceof IJavaObject) {
IJavaObject objectValue= (IJavaObject) value;
try {
@@ -168,6 +169,19 @@ private void resolveFormatter(final IJavaValue value, final IJavaThread thread,
return;
}
}
+ if (value instanceof IJavaPrimitiveValue primeValue) {
+ try {
+ IJavaDebugTarget debugTarget= (IJavaDebugTarget) thread.getDebugTarget();
+ Expression expression= getCompiledExpression(primeValue, debugTarget, thread);
+ if (expression != null) {
+ expression.getEngine().evaluateExpression(expression.getExpression(), primeValue, thread, evaluationListener, DebugEvent.EVALUATION_IMPLICIT, false);
+ return;
+ }
+ } catch (CoreException e) {
+ listener.detailComputed(value, e.toString());
+ return;
+ }
+ }
try {
evaluationListener.valueToString(value);
} catch (DebugException e) {
@@ -181,7 +195,7 @@ private void resolveFormatter(final IJavaValue value, final IJavaThread thread,
}
}
- private IJavaProject getJavaProject(IJavaObject javaValue, IJavaThread thread) throws CoreException {
+ private IJavaProject getJavaProject(IJavaValue javaValue, IJavaThread thread) throws CoreException {
IType type = null;
if (javaValue instanceof IJavaArray) {
@@ -285,11 +299,13 @@ public boolean hasAssociatedDetailFormatter(IJavaType type) {
public DetailFormatter getAssociatedDetailFormatter(IJavaType type) {
String typeName = Util.ZERO_LENGTH_STRING;
try {
- while (type instanceof IJavaArrayType) {
- type = ((IJavaArrayType)type).getComponentType();
+ if (type instanceof IJavaArrayType javaArray) {
+ typeName = javaArray.getName();
}
if (type instanceof IJavaClassType) {
typeName = type.getName();
+ } else if (type instanceof JDIType) {
+ typeName = type.getName();
}
else {
return null;
@@ -299,6 +315,7 @@ public DetailFormatter getAssociatedDetailFormatter(IJavaType type) {
return fDetailFormattersMap.get(typeName);
}
+
public void setAssociatedDetailFormatter(DetailFormatter detailFormatter) {
fDetailFormattersMap.put(detailFormatter.getTypeName(), detailFormatter);
savePreference();
@@ -341,6 +358,23 @@ private String getDetailFormatter(IJavaClassType type) throws DebugException {
return null;
}
+ /**
+ * Return the detail formatter (code snippet) associate with the given type
+ *
+ * @param type
+ * the JDIType type
+ * @return the code snippet for the given JDIType
+ * @throws DebugException
+ * if there is problem computing the snippet
+ */
+ private String getDetailFormatter(JDIType type) throws DebugException {
+ String snippet = getDetailFormatterForPrimitives(type);
+ if (snippet != null) {
+ return snippet;
+ }
+ return null;
+ }
+
/**
* Return the detail formatter (code snippet) associate with
* the given type or one of its super types.
@@ -359,6 +393,34 @@ private String getDetailFormatterSuperClass(IJavaClassType type) throws DebugExc
return getDetailFormatterSuperClass(type.getSuperclass());
}
+ /**
+ * Return the detail formatter (code snippet) associate with the given type or one of its super types.
+ *
+ * @param type
+ * the Array type
+ * @return the snippet for the given class / super class
+ * @throws DebugException
+ * if there is a problem computing the snippet
+ */
+ private String getDetailFormatterFromArray(IJavaArrayType type) throws DebugException {
+ if (type == null) {
+ return null;
+ }
+ DetailFormatter detailFormatter = fDetailFormattersMap.get(type.getName());
+ if (detailFormatter != null && detailFormatter.isEnabled()) {
+ return detailFormatter.getSnippet();
+ }
+ return null;
+ }
+
+ private String getDetailFormatterForPrimitives(JDIType type) throws DebugException {
+ DetailFormatter detailFormatter = fDetailFormattersMap.get(type.getName());
+ if (detailFormatter != null && detailFormatter.isEnabled()) {
+ return detailFormatter.getSnippet();
+ }
+ return null;
+ }
+
/**
* Return the expression which corresponds to the code formatter associated with the type of
* the given object or null
if none.
@@ -385,12 +447,13 @@ private Expression getCompiledExpression(IJavaObject javaObject, IJavaDebugTarge
if (type instanceof IJavaClassType) {
snippet = getDetailFormatter((IJavaClassType) type);
}
- if (type instanceof IJavaArrayType) {
+ if (type instanceof IJavaArrayType jArray) {
if (JavaCore.compareJavaVersions(debugTarget.getVersion(), JavaCore.VERSION_9) < 0) {
snippet = getArraySnippet((IJavaArray) javaObject);
+ } else {
+ snippet = getDetailFormatterFromArray(jArray);
}
}
-
if (snippet != null) {
IJavaProject project = getJavaProject(javaObject, thread);
if (project != null) {
@@ -408,6 +471,101 @@ private Expression getCompiledExpression(IJavaObject javaObject, IJavaDebugTarge
return null;
}
+ /**
+ * Return the expression which corresponds to the code formatter associated with the type of the given primitive or null
if none.
+ *
+ * The code snippet is compiled in the context of the given object.
+ *
+ * @param IJavaPrimitiveValue
+ * the primitive object
+ * @param debugTarget
+ * the target
+ * @param thread
+ * the thread context
+ * @return the compiled expression to be evaluated
+ * @throws CoreException
+ * is a problem occurs compiling the expression
+ */
+ private Expression getCompiledExpression(IJavaPrimitiveValue javaPM, IJavaDebugTarget debugTarget, IJavaThread thread) throws CoreException {
+ IJavaType type = javaPM.getJavaType();
+ if (type == null) {
+ return null;
+ }
+ String snippet = null;
+
+ if (type instanceof JDIType jdiType) {
+ snippet = getDetailFormatter(jdiType);
+ if (snippet == null) {
+ return null;
+ }
+ snippet = primitiveSnippets(snippet, jdiType.getName(), javaPM);
+ }
+ if (type instanceof IJavaArrayType) {
+ if (JavaCore.compareJavaVersions(debugTarget.getVersion(), JavaCore.VERSION_9) < 0) {
+ snippet = getArraySnippet((IJavaArray) javaPM);
+ }
+ }
+ if (snippet != null) {
+ IJavaProject project = getJavaProject(javaPM, thread);
+ if (project != null) {
+ IAstEvaluationEngine evaluationEngine = JDIDebugPlugin.getDefault().getEvaluationEngine(project, debugTarget);
+ ICompiledExpression res = evaluationEngine.getCompiledExpression(snippet, (IJavaStackFrame) thread.getTopStackFrame());
+ if (res != null) {
+ Expression exp = new Expression(res, evaluationEngine);
+ return exp;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return the modified expression which replaces this keyword used in formatter
+ *
+ * The code snippet is compiled in the context of the given object.
+ *
+ * @param snippet
+ * Original snippet
+ * @param typeName
+ * type of the primitive
+ * @param primitiveValue
+ * primitive object
+ * @return modified expression to be evaluated
+ */
+ @SuppressWarnings("nls")
+ private String primitiveSnippets(String snippet, String typeName, IJavaPrimitiveValue primitiveValue) {
+ String modified = null;
+ if (typeName.equals("float")) {
+ String thisValue = Float.valueOf(primitiveValue.getFloatValue()).toString();
+ modified = snippet.replace("this", thisValue);
+ } else if (typeName.equals("int")) {
+ String thisValue = Integer.valueOf(primitiveValue.getIntValue()).toString();
+ modified = snippet.replace("this", thisValue);
+ } else if (typeName.equals("byte")) {
+ String thisValue = Byte.valueOf(primitiveValue.getByteValue()).toString();
+ modified = snippet.replace("this", thisValue);
+ } else if (typeName.equals("long")) {
+ String thisValue = Long.valueOf(primitiveValue.getLongValue()).toString();
+ modified = snippet.replace("this", thisValue);
+ } else if (typeName.equals("short")) {
+ String thisValue = Short.valueOf(primitiveValue.getShortValue()).toString();
+ modified = snippet.replace("this", thisValue);
+ } else if (typeName.equals("double")) {
+ String thisValue = Double.valueOf(primitiveValue.getDoubleValue()).toString();
+ modified = snippet.replace("this", thisValue);
+ } else if (typeName.equals("boolean")) {
+ String thisValue = Boolean.valueOf(primitiveValue.getBooleanValue()).toString();
+ modified = snippet.replace("this", thisValue);
+ } else if (typeName.equals("char")) {
+ String thisValue = Character.valueOf(primitiveValue.getCharValue()).toString();
+ modified = snippet.replace("this", thisValue);
+ } else {
+ modified = snippet;
+ }
+ return modified;
+
+ }
+
protected String getArraySnippet(IJavaArray value) throws DebugException {
String signature = value.getSignature();
int nesting = Signature.getArrayCount(signature);
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaVarActionFilter.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaVarActionFilter.java
index 447fd29a01..c3a61d1a53 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaVarActionFilter.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaVarActionFilter.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2005, 2017 IBM Corporation and others.
+ * Copyright (c) 2005, 2025 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -18,11 +18,13 @@
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.IValue;
+import org.eclipse.jdt.debug.core.IJavaArray;
import org.eclipse.jdt.debug.core.IJavaArrayType;
import org.eclipse.jdt.debug.core.IJavaClassType;
import org.eclipse.jdt.debug.core.IJavaDebugTarget;
import org.eclipse.jdt.debug.core.IJavaFieldVariable;
import org.eclipse.jdt.debug.core.IJavaObject;
+import org.eclipse.jdt.debug.core.IJavaPrimitiveValue;
import org.eclipse.jdt.debug.core.IJavaType;
import org.eclipse.jdt.debug.core.IJavaVariable;
import org.eclipse.jdt.internal.debug.core.model.JDINullValue;
@@ -227,6 +229,14 @@ else if (name.equals("DetailFormatterFilter") && (varValue instanceof IJavaObjec
if(value.equals("inSuperclass")) { //$NON-NLS-1$
return JavaDetailFormattersManager.getDefault().hasSuperclassDetailFormatter(((IJavaObject)varValue).getJavaType());
}
+ } else if (name.equals("DetailFormatterFilter") && (varValue instanceof IJavaPrimitiveValue javaPrime)) { //$NON-NLS-1$
+ if (value.equals("isDefined")) { //$NON-NLS-1$
+ return JavaDetailFormattersManager.getDefault().hasAssociatedDetailFormatter(javaPrime.getJavaType());
+ }
+ } else if (name.equals("DetailFormatterFilter") && (varValue instanceof IJavaArray javaArray)) { //$NON-NLS-1$
+ if (value.equals("isDefined")) { //$NON-NLS-1$
+ return JavaDetailFormattersManager.getDefault().hasAssociatedDetailFormatter(javaArray.getJavaType());
+ }
}
} catch (DebugException e) {}
}
diff --git a/org.eclipse.jdt.debug/.classpath b/org.eclipse.jdt.debug/.classpath
index 3bc414e51b..db8267d6b1 100644
--- a/org.eclipse.jdt.debug/.classpath
+++ b/org.eclipse.jdt.debug/.classpath
@@ -1,6 +1,6 @@
-
+
diff --git a/org.eclipse.jdt.debug/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jdt.debug/.settings/org.eclipse.jdt.core.prefs
index 311a6ec65e..0833f0fb5f 100644
--- a/org.eclipse.jdt.debug/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jdt.debug/.settings/org.eclipse.jdt.core.prefs
@@ -17,9 +17,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=21
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=17
+org.eclipse.jdt.core.compiler.compliance=21
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@@ -129,7 +129,7 @@ org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=error
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.release=enabled
-org.eclipse.jdt.core.compiler.source=17
+org.eclipse.jdt.core.compiler.source=21
org.eclipse.jdt.core.compiler.taskCaseSensitive=enabled
org.eclipse.jdt.core.compiler.taskPriorities=NORMAL,HIGH,NORMAL,HIGH,HIGH
org.eclipse.jdt.core.compiler.taskTags=TODO,FIXME,XXX,EXPERIMENTAL,CONTEXTLAUNCHING
diff --git a/org.eclipse.jdt.debug/META-INF/MANIFEST.MF b/org.eclipse.jdt.debug/META-INF/MANIFEST.MF
index 84d4ebd713..bcbf1729bd 100644
--- a/org.eclipse.jdt.debug/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.debug/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.jdt.debug; singleton:=true
-Bundle-Version: 3.21.700.qualifier
+Bundle-Version: 3.22.0.qualifier
Bundle-ClassPath: jdimodel.jar
Bundle-Activator: org.eclipse.jdt.internal.debug.core.JDIDebugPlugin
Bundle-Vendor: %providerName
@@ -31,6 +31,6 @@ Require-Bundle: org.eclipse.core.resources;bundle-version="[3.19.0,4.0.0)",
org.eclipse.core.runtime;bundle-version="[3.29.0,4.0.0)",
org.eclipse.core.expressions;bundle-version="[3.9.0,4.0.0)"
Bundle-ActivationPolicy: lazy
-Bundle-RequiredExecutionEnvironment: JavaSE-17
+Bundle-RequiredExecutionEnvironment: JavaSE-21
Eclipse-BundleShape: dir
Automatic-Module-Name: org.eclipse.jdt.debug
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/debug/eval/IAstEvaluationEngine.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/debug/eval/IAstEvaluationEngine.java
index 394f322f36..f6a66e89d0 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/debug/eval/IAstEvaluationEngine.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/debug/eval/IAstEvaluationEngine.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2025 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -17,6 +17,7 @@
import org.eclipse.debug.core.DebugException;
import org.eclipse.jdt.debug.core.IJavaObject;
+import org.eclipse.jdt.debug.core.IJavaPrimitiveValue;
import org.eclipse.jdt.debug.core.IJavaReferenceType;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.debug.core.IJavaThread;
@@ -132,6 +133,43 @@ public void evaluateExpression(ICompiledExpression expression,
IEvaluationListener listener, int evaluationDetail,
boolean hitBreakpoints) throws DebugException;
+ /**
+ * Asynchronously evaluates the given expression in the context of the specified type, reporting the result back to the given listener. The
+ * expression is evaluated in the context of the Java project this evaluation engine was created on. If the expression is determined to have no
+ * errors, the expression is evaluated in the thread associated with the given stack frame. When the evaluation completes, the thread will be
+ * suspended at this original location. The thread runs the evaluation with the given evaluation detail (@see
+ * IJavaThread#runEvaluation(IEvaluationRunnable, IProgressMonitor, int)). Compilation and runtime errors are reported in the evaluation result.
+ *
+ * @param expression
+ * the expression to evaluate
+ * @param object
+ * primitive 'this' context for the evaluation
+ * @param thread
+ * the thread in which to run the evaluation, which must be suspended
+ * @param listener
+ * the listener that will receive notification when/if the evaluation completes
+ * @param evaluationDetail
+ * bitmask of one of DebugEvent.EVALUATION
or DebugEvent.EVALUATION_IMPLICIT
and optionally
+ * DISABLE_GC_ON_RESULT
+ * @param hitBreakpoints
+ * whether or not breakpoints should be honored in the evaluation thread during the evaluation. If false
, breakpoints hit
+ * in the evaluation thread will be ignored.
+ * @exception DebugException
+ * if this method fails. Reasons include:
+ *
+ * - Failure communicating with the VM. The DebugException's status code contains the underlying exception responsible for the
+ * failure.
+ * - The associated thread is not currently suspended
+ * - The stack frame is not contained in the debug target associated with this evaluation engine
+ * - The associated thread is suspended in the middle of an evaluation that has not completed. It is not possible to perform
+ * nested evaluations
+ *
+ * @since 3.22
+ */
+ public void evaluateExpression(ICompiledExpression expression,
+ IJavaPrimitiveValue object, IJavaThread thread,
+ IEvaluationListener listener, int evaluationDetail,
+ boolean hitBreakpoints) throws DebugException;
/**
* Synchronously generates a compiled expression from the given expression
* in the context of the specified stack frame. The generated expression can
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTEvaluationEngine.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTEvaluationEngine.java
index 5a398535cf..21c2a7caab 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTEvaluationEngine.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTEvaluationEngine.java
@@ -53,6 +53,7 @@
import org.eclipse.jdt.debug.core.IJavaArrayType;
import org.eclipse.jdt.debug.core.IJavaDebugTarget;
import org.eclipse.jdt.debug.core.IJavaObject;
+import org.eclipse.jdt.debug.core.IJavaPrimitiveValue;
import org.eclipse.jdt.debug.core.IJavaReferenceType;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.debug.core.IJavaThread;
@@ -216,6 +217,25 @@ public void evaluateExpression(ICompiledExpression expression, IJavaObject thisC
doEvaluation(expression, context, thread, listener, evaluationDetail, hitBreakpoints);
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jdt.debug.eval.IAstEvaluationEngine#evaluateExpression(org .eclipse.jdt.debug.eval.ICompiledExpression,
+ * org.eclipse.jdt.debug.core.IJavaPrimitiveValue, org.eclipse.jdt.debug.core.IJavaThread, org.eclipse.jdt.debug.eval.IEvaluationListener, int,
+ * boolean)
+ */
+ @Override
+ public void evaluateExpression(ICompiledExpression expression, IJavaPrimitiveValue thisContext, IJavaThread thread, IEvaluationListener listener, int evaluationDetail, boolean hitBreakpoints) throws DebugException {
+ traceCaller(expression.getSnippet(), thread);
+ IRuntimeContext context = null;
+ if (thisContext instanceof IJavaArray) {
+ context = new ArrayRuntimeContext((IJavaArray) thisContext, thread, getJavaProject());
+ } else {
+ context = new JavaPrimitiveRuntimeContext(thisContext, getJavaProject(), thread);
+ }
+ doEvaluation(expression, context, thread, listener, evaluationDetail, hitBreakpoints);
+ }
+
/**
* Evaluates the given expression in the given thread and the given runtime context.
*/
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/IRuntimeContext.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/IRuntimeContext.java
index 9211dea19a..98f9a1d0c6 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/IRuntimeContext.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/IRuntimeContext.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2025 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -19,6 +19,7 @@
import org.eclipse.jdt.debug.core.IJavaClassObject;
import org.eclipse.jdt.debug.core.IJavaDebugTarget;
import org.eclipse.jdt.debug.core.IJavaObject;
+import org.eclipse.jdt.debug.core.IJavaPrimitiveValue;
import org.eclipse.jdt.debug.core.IJavaReferenceType;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.debug.core.IJavaThread;
@@ -55,6 +56,16 @@ public interface IRuntimeContext {
*/
IJavaObject getThis() throws CoreException;
+ /**
+ * Returns the receiving primitive type in which to perform the evaluation - equivalent to 'this'. Returns null
+ *
+ * @return 'this', or null
+ * @since 3.21
+ */
+ default IJavaPrimitiveValue getThisPrimitive() {
+ return null;
+ }
+
/**
* Returns the receiving type context in which to perform the evaluation. The type of 'this', or in the case of a static context, the class or
* interface in which the evaluation is being performed.
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/JavaPrimitiveRuntimeContext.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/JavaPrimitiveRuntimeContext.java
new file mode 100644
index 0000000000..be0a8290a4
--- /dev/null
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/JavaPrimitiveRuntimeContext.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 2025 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.debug.eval.ast.engine;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.debug.core.IJavaDebugTarget;
+import org.eclipse.jdt.debug.core.IJavaObject;
+import org.eclipse.jdt.debug.core.IJavaPrimitiveValue;
+import org.eclipse.jdt.debug.core.IJavaReferenceType;
+import org.eclipse.jdt.debug.core.IJavaThread;
+import org.eclipse.jdt.debug.core.IJavaVariable;
+
+public class JavaPrimitiveRuntimeContext extends AbstractRuntimeContext {
+ /**
+ * this
object or this context.
+ */
+ private final IJavaPrimitiveValue fThisPrimitive;
+
+ /**
+ * The thread for this context.
+ */
+ private final IJavaThread fThread;
+
+ /**
+ * ObjectValueRuntimeContext constructor.
+ *
+ * @param thisObject
+ * this
object of this context.
+ * @param javaProject
+ * the project for this context.
+ * @param thread
+ * the thread for this context.
+ */
+ public JavaPrimitiveRuntimeContext(IJavaPrimitiveValue thisObject, IJavaProject javaProject, IJavaThread thread) {
+ super(javaProject);
+ fThisPrimitive = thisObject;
+ fThread = thread;
+ }
+
+ /**
+ * @see IRuntimeContext#getVM()
+ */
+ @Override
+ public IJavaDebugTarget getVM() {
+ return (IJavaDebugTarget) fThisPrimitive.getDebugTarget();
+ }
+
+ /**
+ * @see IRuntimeContext#getThis()
+ */
+ @Override
+ public IJavaPrimitiveValue getThisPrimitive() {
+ return fThisPrimitive;
+ }
+
+ /**
+ * @see IRuntimeContext#getReceivingType()
+ */
+ @Override
+ public IJavaReferenceType getReceivingType() throws CoreException {
+ return (IJavaReferenceType) getThisPrimitive().getJavaType();
+ }
+
+ /**
+ * @see IRuntimeContext#getLocals()
+ */
+ @Override
+ public IJavaVariable[] getLocals() {
+ return new IJavaVariable[0];
+ }
+
+ /**
+ * @see IRuntimeContext#getThread()
+ */
+ @Override
+ public IJavaThread getThread() {
+ return fThread;
+ }
+
+ /**
+ * @see IRuntimeContext#isConstructor()
+ */
+ @Override
+ public boolean isConstructor() {
+ return false;
+ }
+
+ @Override
+ public IJavaObject getThis() throws CoreException {
+ return null;
+ }
+
+}
diff --git a/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/ThreadReferenceImpl.java b/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/ThreadReferenceImpl.java
index 67301de32e..6895b7bf32 100644
--- a/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/ThreadReferenceImpl.java
+++ b/org.eclipse.jdt.debug/jdi/org/eclipse/jdi/internal/ThreadReferenceImpl.java
@@ -363,6 +363,7 @@ public boolean isSuspended() {
* @return true if the thread is a virtual thread
* @since 3.20
*/
+ @Override
public boolean isVirtual() {
if (isVirtualCached) {
return isVirtual;