From 0a397a68918ddb6ca4e02c1c2930a22b9bf373a6 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Tue, 14 Feb 2017 12:31:12 +0300 Subject: [PATCH] IDEA-168070 Allow to use expression as a Capture key --- .../tracking/ConstructorInstancesTracker.java | 3 +- .../debugger/memory/utils/StackFrameItem.java | 6 +- .../settings/CaptureConfigurable.java | 8 +- .../debugger/settings/CapturePoint.java | 26 +++-- .../StackCapturingLineBreakpoint.java | 102 ++++++++++-------- 5 files changed, 78 insertions(+), 67 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java index 4899b1ee74fe..343c0e6cd5fd 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java @@ -243,8 +243,7 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp if (myReference.equals(thisRef.referenceType()) && data != null) { thisRef.disableCollection(); myTrackedObjects.add(thisRef); - final List frame = StackFrameItem.createFrames(suspendContext.getThread(), suspendContext, false); - data.getTrackedStacks().addStack(thisRef, frame); + data.getTrackedStacks().addStack(thisRef, StackFrameItem.createFrames(suspendContext, false)); } } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameItem.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameItem.java index 43189dbdfd81..89f88e46ff4f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameItem.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameItem.java @@ -70,10 +70,8 @@ public class StackFrameItem { } @NotNull - public static List createFrames(@Nullable ThreadReferenceProxyImpl threadReferenceProxy, - @NotNull SuspendContextImpl suspendContext, - boolean withVars) - throws EvaluateException { + public static List createFrames(@NotNull SuspendContextImpl suspendContext, boolean withVars) throws EvaluateException { + ThreadReferenceProxyImpl threadReferenceProxy = suspendContext.getThread(); if (threadReferenceProxy != null) { List res = new ArrayList<>(); for (StackFrameProxyImpl frame : threadReferenceProxy.frames()) { diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/CaptureConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/settings/CaptureConfigurable.java index 60a31c993ca8..1c4823bbdd4f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/settings/CaptureConfigurable.java +++ b/java/debugger/impl/src/com/intellij/debugger/settings/CaptureConfigurable.java @@ -204,7 +204,7 @@ public class CaptureConfigurable implements SearchableConfigurable { public static final int INSERT_KEY_EXPR = 6; static final String[] COLUMN_NAMES = - new String[]{"", "Class name", "Method name", "Param index", "Insert class name", "Insert method name", "Insert key expression"}; + new String[]{"", "Class name", "Method name", "Key expression", "Insert class name", "Insert method name", "Insert key expression"}; List myCapturePoints = DebuggerSettings.getInstance().cloneCapturePoints(); public String getColumnName(int column) { @@ -229,7 +229,7 @@ public class CaptureConfigurable implements SearchableConfigurable { case METHOD_COLUMN: return point.myMethodName; case PARAM_COLUMN: - return point.myParamNo; + return point.myCaptureKeyExpression; case INSERT_CLASS_COLUMN: return point.myInsertClassName; case INSERT_METHOD_COLUMN: @@ -257,7 +257,7 @@ public class CaptureConfigurable implements SearchableConfigurable { point.myMethodName = (String)value; break; case PARAM_COLUMN: - point.myParamNo = (int)value; + point.myCaptureKeyExpression = (String)value; break; case INSERT_CLASS_COLUMN: point.myInsertClassName = (String)value; @@ -276,8 +276,6 @@ public class CaptureConfigurable implements SearchableConfigurable { switch (columnIndex) { case ENABLED_COLUMN: return Boolean.class; - case PARAM_COLUMN: - return Integer.class; } return String.class; } diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/CapturePoint.java b/java/debugger/impl/src/com/intellij/debugger/settings/CapturePoint.java index 3c0f27143d41..55d16073217d 100644 --- a/java/debugger/impl/src/com/intellij/debugger/settings/CapturePoint.java +++ b/java/debugger/impl/src/com/intellij/debugger/settings/CapturePoint.java @@ -32,8 +32,8 @@ public class CapturePoint implements Cloneable { @Attribute("method-name") public String myMethodName; - @Attribute("param-idx") - public int myParamNo = 0; + @Attribute("capture-key-expression") + public String myCaptureKeyExpression; @Attribute("insert-class-name") public String myInsertClassName; @@ -49,15 +49,19 @@ public class CapturePoint implements Cloneable { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - CapturePoint point = (CapturePoint)o; + CapturePoint that = (CapturePoint)o; - if (myEnabled != point.myEnabled) return false; - if (myParamNo != point.myParamNo) return false; - if (myClassName != null ? !myClassName.equals(point.myClassName) : point.myClassName != null) return false; - if (myMethodName != null ? !myMethodName.equals(point.myMethodName) : point.myMethodName != null) return false; - if (myInsertClassName != null ? !myInsertClassName.equals(point.myInsertClassName) : point.myInsertClassName != null) return false; - if (myInsertMethodName != null ? !myInsertMethodName.equals(point.myInsertMethodName) : point.myInsertMethodName != null) return false; - if (myInsertKeyExpression != null ? !myInsertKeyExpression.equals(point.myInsertKeyExpression) : point.myInsertKeyExpression != null) { + if (myEnabled != that.myEnabled) return false; + if (myClassName != null ? !myClassName.equals(that.myClassName) : that.myClassName != null) return false; + if (myMethodName != null ? !myMethodName.equals(that.myMethodName) : that.myMethodName != null) return false; + if (myCaptureKeyExpression != null + ? !myCaptureKeyExpression.equals(that.myCaptureKeyExpression) + : that.myCaptureKeyExpression != null) { + return false; + } + if (myInsertClassName != null ? !myInsertClassName.equals(that.myInsertClassName) : that.myInsertClassName != null) return false; + if (myInsertMethodName != null ? !myInsertMethodName.equals(that.myInsertMethodName) : that.myInsertMethodName != null) return false; + if (myInsertKeyExpression != null ? !myInsertKeyExpression.equals(that.myInsertKeyExpression) : that.myInsertKeyExpression != null) { return false; } @@ -69,7 +73,7 @@ public class CapturePoint implements Cloneable { int result = (myEnabled ? 1 : 0); result = 31 * result + (myClassName != null ? myClassName.hashCode() : 0); result = 31 * result + (myMethodName != null ? myMethodName.hashCode() : 0); - result = 31 * result + myParamNo; + result = 31 * result + (myCaptureKeyExpression != null ? myCaptureKeyExpression.hashCode() : 0); result = 31 * result + (myInsertClassName != null ? myInsertClassName.hashCode() : 0); result = 31 * result + (myInsertMethodName != null ? myInsertMethodName.hashCode() : 0); result = 31 * result + (myInsertKeyExpression != null ? myInsertKeyExpression.hashCode() : 0); diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/StackCapturingLineBreakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/StackCapturingLineBreakpoint.java index a5a4a733a9d0..15f2c103572b 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/StackCapturingLineBreakpoint.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/StackCapturingLineBreakpoint.java @@ -15,33 +15,31 @@ */ package com.intellij.debugger.ui.breakpoints; +import com.intellij.debugger.SourcePosition; +import com.intellij.debugger.engine.ContextUtil; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.DebuggerManagerThreadImpl; import com.intellij.debugger.engine.SuspendContextImpl; -import com.intellij.debugger.engine.evaluation.CodeFragmentKind; -import com.intellij.debugger.engine.evaluation.EvaluateException; -import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; -import com.intellij.debugger.engine.evaluation.TextWithImportsImpl; +import com.intellij.debugger.engine.evaluation.*; import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl; import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; +import com.intellij.debugger.impl.DebuggerUtilsEx; import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.debugger.memory.utils.StackFrameItem; import com.intellij.debugger.settings.CapturePoint; import com.intellij.debugger.settings.DebuggerSettings; -import com.intellij.openapi.application.ReadAction; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.NullableLazyValue; +import com.intellij.openapi.util.ThrowableComputable; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiElement; import com.intellij.util.containers.ContainerUtil; -import com.sun.jdi.Location; -import com.sun.jdi.Method; -import com.sun.jdi.ObjectReference; -import com.sun.jdi.Value; +import com.sun.jdi.*; import com.sun.jdi.event.LocatableEvent; import one.util.streamex.StreamEx; import org.jetbrains.annotations.NotNull; @@ -63,10 +61,11 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint { private final CapturePoint myCapturePoint; private final String mySignature; - private final NullableLazyValue myEvaluator; + private final MyEvaluator myCaptureEvaluator; + private final MyEvaluator myInsertEvaluator; public static final Key> CAPTURE_BREAKPOINTS = Key.create("CAPTURE_BREAKPOINTS"); - public static final Key>> CAPTURED_STACKS = Key.create("CAPTURED_STACKS"); + private static final Key>> CAPTURED_STACKS = Key.create("CAPTURED_STACKS"); private static final int MAX_STORED_STACKS = 1000; public static final int MAX_STACK_LENGTH = 500; @@ -81,16 +80,8 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint { myProperties.myClassPattern = myCapturePoint.myClassName; myProperties.myMethodName = myCapturePoint.myMethodName; - myEvaluator = NullableLazyValue.createValue(() -> ReadAction.compute(() -> { - try { - return EvaluatorBuilderImpl.build(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, myCapturePoint.myInsertKeyExpression), - null, null, project); - } - catch (EvaluateException e) { - LOG.warn(e); - } - return null; - })); + myCaptureEvaluator = new MyEvaluator(myCapturePoint.myCaptureKeyExpression); + myInsertEvaluator = new MyEvaluator(myCapturePoint.myInsertKeyExpression); } @NotNull @@ -112,23 +103,24 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint { StackFrameProxyImpl frameProxy = suspendContext.getFrameProxy(); if (frameProxy != null) { DebugProcessImpl process = suspendContext.getDebugProcess(); - Map> stacks = process.getUserData(CAPTURED_STACKS); + Map> stacks = process.getUserData(CAPTURED_STACKS); if (stacks == null) { stacks = new CapturedStacksMap(); process.putUserData(CAPTURED_STACKS, Collections.synchronizedMap(stacks)); } - Value key = ContainerUtil.getOrElse(frameProxy.getArgumentValues(), myCapturePoint.myParamNo, null); + Value key = myCaptureEvaluator.evaluate(new EvaluationContextImpl(suspendContext, frameProxy, frameProxy.thisObject())); if (key instanceof ObjectReference) { - List frames = StackFrameItem.createFrames(suspendContext.getThread(), suspendContext, true); + List frames = StackFrameItem.createFrames(suspendContext, true); if (frames.size() > MAX_STACK_LENGTH) { frames = frames.subList(0, MAX_STACK_LENGTH); } - stacks.put((ObjectReference)key, frames); + stacks.put(getKey((ObjectReference)key), frames); } } } } - catch (EvaluateException ignored) { + catch (EvaluateException e) { + LOG.debug(e); } return false; } @@ -137,7 +129,7 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint { protected void fireBreakpointChanged() { } - private static class CapturedStacksMap extends LinkedHashMap> { + private static class CapturedStacksMap extends LinkedHashMap> { @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_STORED_STACKS; @@ -186,7 +178,7 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint { public static List getRelatedStack(@Nullable StackFrameProxyImpl frame, @NotNull SuspendContextImpl suspendContext) { if (frame != null) { DebugProcessImpl debugProcess = suspendContext.getDebugProcess(); - Map> capturedStacks = debugProcess.getUserData(CAPTURED_STACKS); + Map> capturedStacks = debugProcess.getUserData(CAPTURED_STACKS); if (ContainerUtil.isEmpty(capturedStacks)) { return null; } @@ -198,33 +190,25 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint { Location location = frame.location(); String className = location.declaringType().name(); String methodName = location.method().name(); - List argumentValues = null; for (StackCapturingLineBreakpoint b : captureBreakpoints) { String insertClassName = b.myCapturePoint.myInsertClassName; if ((StringUtil.isEmpty(insertClassName) || StringUtil.equals(insertClassName, className)) && StringUtil.equals(b.myCapturePoint.myInsertMethodName, methodName)) { - if (argumentValues == null) { - argumentValues = frame.getArgumentValues(); - } - try { - ExpressionEvaluator evaluator = b.myEvaluator.getValue(); - if (evaluator != null) { - EvaluationContextImpl evaluationContext = new EvaluationContextImpl(suspendContext, frame, frame.thisObject()); - Value key = evaluator.evaluate(evaluationContext); - - if (key instanceof ObjectReference) { - return capturedStacks.get(key); - } + Value key = b.myInsertEvaluator.evaluate(new EvaluationContextImpl(suspendContext, frame, frame.thisObject())); + if (key instanceof ObjectReference) { + return capturedStacks.get(getKey((ObjectReference)key)); } } - catch (EvaluateException ignore) { + catch (EvaluateException e) { + LOG.debug(e); } } } } - catch (EvaluateException ignore) { + catch (EvaluateException e) { + LOG.debug(e); } } return null; @@ -233,11 +217,39 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint { @Nullable public static List getRelatedStack(@Nullable ObjectReference key, @Nullable DebugProcessImpl process) { if (process != null && key != null) { - Map> data = process.getUserData(CAPTURED_STACKS); + Map> data = process.getUserData(CAPTURED_STACKS); if (data != null) { return data.get(key); } } return null; } + + private static Object getKey(ObjectReference reference) { + return reference instanceof StringReference ? ((StringReference)reference).value() : reference; + } + + private static class MyEvaluator { + private final String myExpression; + ExpressionEvaluator myEvaluator; + + public MyEvaluator(String expression) { + myExpression = expression; + } + + Value evaluate(final EvaluationContext context) throws EvaluateException { + if (myEvaluator == null) { + myEvaluator = ApplicationManager.getApplication().runReadAction( + (ThrowableComputable)() -> { + SourcePosition sourcePosition = ContextUtil.getSourcePosition(context); + PsiElement contextElement = ContextUtil.getContextElement(sourcePosition); + return EvaluatorBuilderImpl.build( + new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, myExpression), contextElement, sourcePosition, context.getProject()); + }); + } + Value value = myEvaluator.evaluate(context); + DebuggerUtilsEx.keep(value, context); + return value; + } + } }