IDEA-162947 Add ability to use lambda expressions in breakpoint conditions
authorEgor.Ushakov <egor.ushakov@jetbrains.com>
Mon, 24 Oct 2016 16:08:26 +0000 (19:08 +0300)
committerEgor.Ushakov <egor.ushakov@jetbrains.com>
Mon, 24 Oct 2016 16:09:47 +0000 (19:09 +0300)
- support log expressions as well

java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java
java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java
java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java

index f4f9ac95bb68f2bf1ef3c78e6430ddcd7e3a9092..3ba029ce380d95949c71980d2b2631e8862cd7cb 100644 (file)
@@ -25,12 +25,14 @@ import com.intellij.debugger.engine.*;
 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.evaluation.expression.UnsupportedExpressionException;
 import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
 import com.intellij.debugger.impl.DebuggerUtilsEx;
 import com.intellij.debugger.jdi.StackFrameProxyImpl;
 import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
 import com.intellij.debugger.requests.ClassPrepareRequestor;
 import com.intellij.debugger.settings.DebuggerSettings;
+import com.intellij.debugger.ui.impl.watch.EvaluationDescriptor;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Computable;
@@ -38,6 +40,7 @@ import com.intellij.openapi.util.InvalidDataException;
 import com.intellij.openapi.util.JDOMExternalizerUtil;
 import com.intellij.openapi.util.Key;
 import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiCodeFragment;
 import com.intellij.psi.PsiElement;
 import com.intellij.ui.AppUIUtil;
 import com.intellij.ui.classFilter.ClassFilter;
@@ -63,6 +66,7 @@ import org.jetbrains.java.debugger.breakpoints.properties.JavaBreakpointProperti
 
 import javax.swing.*;
 import java.util.Arrays;
+import java.util.function.Function;
 
 public abstract class Breakpoint<P extends JavaBreakpointProperties> implements FilteredRequestor, ClassPrepareRequestor {
   public static final Key<Breakpoint> DATA_KEY = Key.create("JavaBreakpoint");
@@ -264,8 +268,8 @@ public abstract class Breakpoint<P extends JavaBreakpointProperties> implements
           try {
             SourcePosition position = ContextUtil.getSourcePosition(context);
             PsiElement element = ContextUtil.getContextElement(context, position);
-            ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject,
-              () -> EvaluatorBuilderImpl.build(expressionToEvaluate, element, position, myProject));
+            ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, () ->
+              createExpressionEvaluator(myProject, context, element, position, expressionToEvaluate, this::createLogMessageCodeFragment));
             Value eval = evaluator.evaluate(context);
             buf.append(eval instanceof VoidValue ? "void" : DebuggerUtils.getValueAsString(context, eval));
           }
@@ -348,7 +352,8 @@ public abstract class Breakpoint<P extends JavaBreakpointProperties> implements
         if (contextPsiElement == null) {
           contextPsiElement = getEvaluationElement(); // as a last resort
         }
-        return EvaluatorBuilderImpl.build(getCondition(), contextPsiElement, contextSourcePosition, project);
+        return createExpressionEvaluator(project, context, contextPsiElement, contextSourcePosition, getCondition(),
+                                         this::createConditionCodeFragment);
       });
       return DebuggerUtilsEx.evaluateBoolean(evaluator, context);
     }
@@ -362,6 +367,37 @@ public abstract class Breakpoint<P extends JavaBreakpointProperties> implements
     }
   }
 
+  private static ExpressionEvaluator createExpressionEvaluator(Project project,
+                                                               EvaluationContextImpl context,
+                                                               PsiElement contextPsiElement,
+                                                               SourcePosition contextSourcePosition,
+                                                               TextWithImports text,
+                                                               Function<PsiElement, PsiCodeFragment> fragmentFactory)
+    throws EvaluateException {
+    try {
+      return EvaluatorBuilderImpl.build(text, contextPsiElement, contextSourcePosition, project);
+    }
+    catch (UnsupportedExpressionException ex) {
+      ExpressionEvaluator eval = EvaluationDescriptor.createCompilingEvaluator(context, contextPsiElement, fragmentFactory);
+      if (eval != null) {
+        return eval;
+      }
+      throw ex;
+    }
+  }
+
+  private PsiCodeFragment createConditionCodeFragment(PsiElement context) {
+    return createCodeFragment(getProject(), getCondition(), context);
+  }
+
+  private PsiCodeFragment createLogMessageCodeFragment(PsiElement context) {
+    return createCodeFragment(getProject(), getLogMessage(), context);
+  }
+
+  private static PsiCodeFragment createCodeFragment(Project project, TextWithImports text, PsiElement context) {
+    return DebuggerUtilsEx.findAppropriateCodeFragmentFactory(text, context).createCodeFragment(text, context, project);
+  }
+
   protected String calculateEventClass(EvaluationContextImpl context, LocatableEvent event) throws EvaluateException {
     return event.location().declaringType().name();
   }
index 05464f5e77dcfc4ac231cce83d020b08006f055b..e9f4592b5e9ff8e98687aa83c69fab0c28aa2c0f 100644 (file)
@@ -30,7 +30,9 @@ import com.intellij.debugger.engine.evaluation.expression.UnsupportedExpressionE
 import com.intellij.debugger.impl.DebuggerContextImpl;
 import com.intellij.debugger.impl.DebuggerUtilsEx;
 import com.intellij.debugger.jdi.StackFrameProxyImpl;
+import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.ThrowableComputable;
 import com.intellij.openapi.util.registry.Registry;
 import com.intellij.psi.*;
 import com.intellij.refactoring.extractMethod.PrepareFailedException;
@@ -40,10 +42,12 @@ import com.sun.jdi.*;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.util.function.Function;
+
 /**
  * @author lex
  */
-public abstract class EvaluationDescriptor extends ValueDescriptorImpl{
+public abstract class EvaluationDescriptor extends ValueDescriptorImpl {
   private Modifier myModifier;
   protected TextWithImports myText;
 
@@ -58,7 +62,7 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl{
     myText = text;
   }
 
-  protected abstract EvaluationContextImpl getEvaluationContext (EvaluationContextImpl evaluationContext);
+  protected abstract EvaluationContextImpl getEvaluationContext(EvaluationContextImpl evaluationContext);
 
   protected abstract PsiCodeFragment getEvaluationCode(StackFrameContext context) throws EvaluateException;
 
@@ -73,35 +77,15 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl{
       SourcePosition position = ContextUtil.getSourcePosition(evaluationContext);
       PsiElement psiContext = ContextUtil.getContextElement(evaluationContext, position);
 
-      ExpressionEvaluator evaluator = null;
+      ExpressionEvaluator evaluator;
       try {
-        evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, new EvaluatingComputable<ExpressionEvaluator>() {
-          public ExpressionEvaluator compute() throws EvaluateException {
-            return DebuggerUtilsEx.findAppropriateCodeFragmentFactory(getEvaluationText(), psiContext).getEvaluatorBuilder().
-              build(getEvaluationCode(thisEvaluationContext), position);
-          }
-        });
+        evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, () ->
+          DebuggerUtilsEx.findAppropriateCodeFragmentFactory(getEvaluationText(), psiContext)
+            .getEvaluatorBuilder()
+            .build(getEvaluationCode(thisEvaluationContext), position));
       }
       catch (UnsupportedExpressionException ex) {
-        if (Registry.is("debugger.compiling.evaluator") && psiContext != null) {
-          evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, new EvaluatingComputable<ExpressionEvaluator>() {
-            public ExpressionEvaluator compute() throws EvaluateException {
-              PsiFile psiFile = psiContext.getContainingFile();
-              PsiCodeFragment fragment = createCodeFragment(psiContext);
-              try {
-                ExtractLightMethodObjectHandler.ExtractedData data = ExtractLightMethodObjectHandler.extractLightMethodObject(myProject,
-                                                                     psiFile, fragment, CompilingEvaluator.getGeneratedClassName());
-                if (data != null) {
-                  return new CompilingEvaluatorImpl(evaluationContext, psiContext, data);
-                }
-              }
-              catch (PrepareFailedException e) {
-                LOG.info(e);
-              }
-              return null;
-            }
-          });
-        }
+        evaluator = createCompilingEvaluator(evaluationContext, psiContext, this::createCodeFragment);
         if (evaluator == null) {
           throw ex;
         }
@@ -131,9 +115,35 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl{
     }
   }
 
+  @Nullable
+  public static ExpressionEvaluator createCompilingEvaluator(EvaluationContextImpl evaluationContext,
+                                                             @Nullable PsiElement psiContext,
+                                                             Function<PsiElement, PsiCodeFragment> fragmentFactory)
+    throws EvaluateException {
+    if (Registry.is("debugger.compiling.evaluator") && psiContext != null) {
+      return ApplicationManager.getApplication().runReadAction((ThrowableComputable<ExpressionEvaluator, EvaluateException>)() -> {
+        try {
+          ExtractLightMethodObjectHandler.ExtractedData data = ExtractLightMethodObjectHandler.extractLightMethodObject(
+            evaluationContext.getProject(),
+            psiContext.getContainingFile(),
+            fragmentFactory.apply(psiContext),
+            CompilingEvaluator.getGeneratedClassName());
+          if (data != null) {
+            return new CompilingEvaluatorImpl(evaluationContext, psiContext, data);
+          }
+        }
+        catch (PrepareFailedException e) {
+          LOG.info(e);
+        }
+        return null;
+      });
+    }
+    return null;
+  }
+
   public PsiExpression getDescriptorEvaluation(DebuggerContext context) throws EvaluateException {
     PsiElement evaluationCode = getEvaluationCode(context);
-    if(evaluationCode instanceof PsiExpressionCodeFragment) {
+    if (evaluationCode instanceof PsiExpressionCodeFragment) {
       return ((PsiExpressionCodeFragment)evaluationCode).getExpression();
     }
     else {
@@ -171,10 +181,10 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl{
             }
 
             public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String className) throws InvocationException,
-                                                                                                             ClassNotLoadedException,
-                                                                                                             IncompatibleThreadStateException,
-                                                                                                             InvalidTypeException,
-                                                                                                             EvaluateException {
+                                                                                                      ClassNotLoadedException,
+                                                                                                      IncompatibleThreadStateException,
+                                                                                                      InvalidTypeException,
+                                                                                                      EvaluateException {
               return evaluationContext.getDebugProcess().loadClass(evaluationContext, className,
                                                                    evaluationContext.getClassLoader());
             }
index 90a81779dbb43d3d1bd0cdb500659784bd91f21c..bbc8d70005975c77e995953e58f88f5323888eea 100644 (file)
@@ -47,6 +47,7 @@ import com.intellij.util.SmartList;
 import com.intellij.util.TimeoutUtil;
 import com.intellij.util.lang.CompoundRuntimeException;
 import com.intellij.util.ui.UIUtil;
+import com.intellij.xdebugger.breakpoints.XBreakpoint;
 import com.sun.jdi.Method;
 import com.sun.jdi.ThreadReference;
 
@@ -430,6 +431,13 @@ public abstract class ExecutionWithDebuggerToolsTestCase extends ExecutionTestCa
           breakpoint.setCondition(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, condition));
           println("Condition = " + condition, ProcessOutputTypes.SYSTEM);
         }
+
+        String logExpression = readValue(comment, "LogExpression");
+        if (logExpression != null) {
+          breakpoint.getXBreakpoint().setLogExpression(logExpression);
+          println("LogExpression = " + logExpression, ProcessOutputTypes.SYSTEM);
+        }
+
         String passCount = readValue(comment, "Pass count");
         if (passCount != null) {
           breakpoint.setCountFilterEnabled(true);