IDEA-162947 Add ability to use lambda expressions in breakpoint conditions

- support log expressions as well
This commit is contained in:
Egor.Ushakov
2016-10-24 19:09:47 +03:00
parent 81c423a5d6
commit afd3f32362
3 changed files with 90 additions and 36 deletions
@@ -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();
}
@@ -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());
}