[debugger] store vm related user data in virtual machine proxy, not in debug process

GitOrigin-RevId: d4ea94a732775782f75ea0cda6f5105328699b0e
This commit is contained in:
Egor Ushakov
2025-02-14 17:06:09 +01:00
committed by intellij-monorepo-bot
parent 97265aee33
commit 431ced79bc
6 changed files with 87 additions and 96 deletions

View File

@@ -95,7 +95,8 @@ public final class AsyncStacksUtils {
EvaluationContextImpl evaluationContext = evalContext.withAutoLoadClasses(false);
DebugProcessImpl process = evaluationContext.getDebugProcess();
Pair<ClassType, Method> methodPair = process.getUserData(CAPTURE_STORAGE_METHOD);
VirtualMachineProxyImpl virtualMachineProxy = evalContext.getVirtualMachineProxy();
Pair<ClassType, Method> methodPair = virtualMachineProxy.getUserData(CAPTURE_STORAGE_METHOD);
if (methodPair == null) {
try {
@@ -112,14 +113,13 @@ public final class AsyncStacksUtils {
methodPair = NO_CAPTURE_AGENT;
LOG.debug("Error loading debug agent", e);
}
putProcessUserData(CAPTURE_STORAGE_METHOD, methodPair, process);
virtualMachineProxy.putUserData(CAPTURE_STORAGE_METHOD, methodPair);
}
if (methodPair == NO_CAPTURE_AGENT) {
return null;
}
VirtualMachineProxyImpl virtualMachineProxy = evalContext.getVirtualMachineProxy();
List<Value> args = Collections.singletonList(virtualMachineProxy.mirrorOf(getMaxStackLength()));
Pair<ClassType, Method> finalMethodPair = methodPair;
String value = DebuggerUtils.getInstance().processCollectibleValue(

View File

@@ -980,7 +980,6 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
myReturnValueWatcher = null;
myNodeRenderersMap.clear();
myRenderers.clear();
DebuggerUtils.cleanupAfterProcessFinish(this);
myState.compareAndSet(State.DETACHING, State.DETACHED);
try {
forEachSafe(myDebugProcessListeners, it -> it.processDetached(this, closedByUser));

View File

@@ -375,13 +375,80 @@ public final class DebuggerUtilsImpl extends DebuggerUtilsEx {
}
}
private static final Key<Method> TO_STRING_METHOD_KEY = new Key<>("CachedToStringMethod");
@Override
protected Value internalInvokeInstanceMethod(@NotNull EvaluationContext evaluationContext,
@NotNull ObjectReference objRef,
@NotNull Method method,
@NotNull List<? extends Value> args) throws EvaluateException {
return ((EvaluationContextImpl)evaluationContext).getDebugProcess()
.invokeInstanceMethod(evaluationContext, objRef, method, args, 0, true);
protected String getValueAsStringImpl(@NotNull EvaluationContext evaluationContext, @Nullable Value value) throws EvaluateException {
try {
if (value == null) {
return "null";
}
if (value instanceof StringReference) {
ensureNotInsideObjectConstructor((ObjectReference)value, evaluationContext);
return ((StringReference)value).value();
}
if (isInteger(value)) {
return String.valueOf(((PrimitiveValue)value).longValue());
}
if (value instanceof FloatValue) {
return String.valueOf(((FloatValue)value).floatValue());
}
if (value instanceof DoubleValue) {
return String.valueOf(((DoubleValue)value).doubleValue());
}
if (value instanceof BooleanValue) {
return String.valueOf(((PrimitiveValue)value).booleanValue());
}
if (value instanceof CharValue) {
return String.valueOf(((PrimitiveValue)value).charValue());
}
if (value instanceof ObjectReference objRef) {
// We can not pretty print arrays here, otherwise evaluation may fail unexpectedly, check IDEA-358202
//if (value instanceof ArrayReference arrayRef) {
// final StringJoiner joiner = new StringJoiner(",", "[", "]");
// for (final Value element : arrayRef.getValues()) {
// joiner.add(getValueAsString(evaluationContext, element));
// }
// return joiner.toString();
//}
EvaluationContextImpl evaluationContextImpl = (EvaluationContextImpl)evaluationContext;
VirtualMachineProxyImpl virtualMachineProxy = evaluationContextImpl.getVirtualMachineProxy();
Method toStringMethod = virtualMachineProxy.getUserData(TO_STRING_METHOD_KEY);
if (toStringMethod == null) {
try {
ReferenceType refType = getObjectClassType(objRef.virtualMachine());
toStringMethod = findMethod(refType, "toString", "()Ljava/lang/String;");
virtualMachineProxy.putUserData(TO_STRING_METHOD_KEY, toStringMethod);
}
catch (Exception ignored) {
throw EvaluateExceptionUtil.createEvaluateException(
JavaDebuggerBundle.message("evaluation.error.cannot.evaluate.tostring", objRef.referenceType().name()));
}
}
if (toStringMethod == null) {
throw EvaluateExceptionUtil.createEvaluateException(
JavaDebuggerBundle.message("evaluation.error.cannot.evaluate.tostring", objRef.referenceType().name()));
}
Method finalToStringMethod = toStringMethod;
return processCollectibleValue(
() -> evaluationContextImpl.getDebugProcess()
.invokeInstanceMethod(evaluationContext, objRef, finalToStringMethod, Collections.emptyList(), 0, true),
result -> {
// while result must be of com.sun.jdi.StringReference type, it turns out that sometimes (jvm bugs?)
// it is a plain com.sun.tools.jdi.ObjectReferenceImpl
if (result == null) {
return "null";
}
return result instanceof StringReference ? ((StringReference)result).value() : result.toString();
},
evaluationContext);
}
throw EvaluateExceptionUtil.createEvaluateException(JavaDebuggerBundle.message("evaluation.error.unsupported.expression.type"));
}
catch (ObjectCollectedException ignored) {
throw EvaluateExceptionUtil.OBJECT_WAS_COLLECTED;
}
}
// compilable version of array class for compiling evaluator

View File

@@ -9,6 +9,7 @@ import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.impl.DebuggerUtilsImpl;
import com.intellij.debugger.impl.MethodNotFoundException;
import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.IntelliJProjectUtil;
@@ -59,12 +60,13 @@ public final class BatchEvaluator {
}
}
public static BatchEvaluator getBatchEvaluator(DebugProcess debugProcess) {
BatchEvaluator batchEvaluator = debugProcess.getUserData(BATCH_EVALUATOR_KEY);
public static BatchEvaluator getBatchEvaluator(EvaluationContext evaluationContext) {
VirtualMachineProxyImpl virtualMachineProxy = ((EvaluationContextImpl)evaluationContext).getVirtualMachineProxy();
BatchEvaluator batchEvaluator = virtualMachineProxy.getUserData(BATCH_EVALUATOR_KEY);
if (batchEvaluator == null) {
batchEvaluator = new BatchEvaluator();
debugProcess.putUserData(BATCH_EVALUATOR_KEY, batchEvaluator);
virtualMachineProxy.putUserData(BATCH_EVALUATOR_KEY, batchEvaluator);
}
return batchEvaluator;
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.debugger.ui.tree.render;
import com.intellij.debugger.DebuggerContext;
@@ -81,7 +81,7 @@ public class ToStringRenderer extends NodeRendererImpl implements OnDemandRender
if (value instanceof ObjectReference) {
DebuggerUtils.ensureNotInsideObjectConstructor((ObjectReference)value, evaluationContext);
}
BatchEvaluator.getBatchEvaluator(evaluationContext.getDebugProcess()).invoke(new ToStringCommand(evaluationContext, value) {
BatchEvaluator.getBatchEvaluator(evaluationContext).invoke(new ToStringCommand(evaluationContext, value) {
@Override
public void evaluationResult(String message) {
valueDescriptor.setValueLabel(

View File

@@ -16,7 +16,6 @@ import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.registry.Registry;
@@ -44,7 +43,6 @@ import java.util.function.Function;
public abstract class DebuggerUtils {
private static final Logger LOG = Logger.getInstance(DebuggerUtils.class);
private static final Key<Method> TO_STRING_METHOD_KEY = new Key<>("CachedToStringMethod");
public static final Set<String> ourPrimitiveTypeNames = Set.of(
"byte", "short", "int", "long", "float", "double", "boolean", "char"
);
@@ -66,86 +64,11 @@ public abstract class DebuggerUtils {
return Registry.is("debugger.new.suspend.state.tracking");
}
public static void cleanupAfterProcessFinish(DebugProcess debugProcess) {
debugProcess.putUserData(TO_STRING_METHOD_KEY, null);
public static @NonNls String getValueAsString(@NotNull EvaluationContext evaluationContext, @Nullable Value value) throws EvaluateException {
return getInstance().getValueAsStringImpl(evaluationContext, value);
}
public static @NonNls String getValueAsString(final EvaluationContext evaluationContext, Value value) throws EvaluateException {
try {
if (value == null) {
return "null";
}
if (value instanceof StringReference) {
ensureNotInsideObjectConstructor((ObjectReference)value, evaluationContext);
return ((StringReference)value).value();
}
if (isInteger(value)) {
return String.valueOf(((PrimitiveValue)value).longValue());
}
if (value instanceof FloatValue) {
return String.valueOf(((FloatValue)value).floatValue());
}
if (value instanceof DoubleValue) {
return String.valueOf(((DoubleValue)value).doubleValue());
}
if (value instanceof BooleanValue) {
return String.valueOf(((PrimitiveValue)value).booleanValue());
}
if (value instanceof CharValue) {
return String.valueOf(((PrimitiveValue)value).charValue());
}
if (value instanceof ObjectReference objRef) {
// We can not pretty print arrays here, otherwise evaluation may fail unexpectedly, check IDEA-358202
//if (value instanceof ArrayReference arrayRef) {
// final StringJoiner joiner = new StringJoiner(",", "[", "]");
// for (final Value element : arrayRef.getValues()) {
// joiner.add(getValueAsString(evaluationContext, element));
// }
// return joiner.toString();
//}
final DebugProcess debugProcess = evaluationContext.getDebugProcess();
Method toStringMethod = debugProcess.getUserData(TO_STRING_METHOD_KEY);
if (toStringMethod == null || !toStringMethod.virtualMachine().equals(objRef.virtualMachine())) {
try {
ReferenceType refType = getObjectClassType(objRef.virtualMachine());
toStringMethod = findMethod(refType, "toString", "()Ljava/lang/String;");
debugProcess.putUserData(TO_STRING_METHOD_KEY, toStringMethod);
}
catch (Exception ignored) {
throw EvaluateExceptionUtil.createEvaluateException(
JavaDebuggerBundle.message("evaluation.error.cannot.evaluate.tostring", objRef.referenceType().name()));
}
}
if (toStringMethod == null) {
throw EvaluateExceptionUtil.createEvaluateException(
JavaDebuggerBundle.message("evaluation.error.cannot.evaluate.tostring", objRef.referenceType().name()));
}
Method finalToStringMethod = toStringMethod;
DebuggerUtils instance = getInstance();
return instance.processCollectibleValue(
() -> instance.internalInvokeInstanceMethod(evaluationContext, objRef, finalToStringMethod, Collections.emptyList()),
result -> {
// while result must be of com.sun.jdi.StringReference type, it turns out that sometimes (jvm bugs?)
// it is a plain com.sun.tools.jdi.ObjectReferenceImpl
if (result == null) {
return "null";
}
return result instanceof StringReference ? ((StringReference)result).value() : result.toString();
},
evaluationContext);
}
throw EvaluateExceptionUtil.createEvaluateException(JavaDebuggerBundle.message("evaluation.error.unsupported.expression.type"));
}
catch (ObjectCollectedException ignored) {
throw EvaluateExceptionUtil.OBJECT_WAS_COLLECTED;
}
}
protected abstract Value internalInvokeInstanceMethod(@NotNull EvaluationContext evaluationContext,
@NotNull ObjectReference objRef,
@NotNull Method method,
@NotNull List<? extends Value> args) throws EvaluateException;
protected abstract @NonNls String getValueAsStringImpl(@NotNull EvaluationContext evaluationContext, @Nullable Value value) throws EvaluateException;
@ApiStatus.Internal
public abstract <R, T> R processCollectibleValue(
@@ -385,7 +308,7 @@ public abstract class DebuggerUtils {
return getSuperTypeInt(subType, superType);
}
private static ReferenceType getObjectClassType(VirtualMachine virtualMachine) {
protected static ReferenceType getObjectClassType(VirtualMachine virtualMachine) {
return ContainerUtil.getFirstItem(virtualMachine.classesByName(CommonClassNames.JAVA_LANG_OBJECT));
}