diff options
Diffstat (limited to 'java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java')
-rw-r--r-- | java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java | 385 |
1 files changed, 350 insertions, 35 deletions
diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java index 1b370ca47ded..bbbcf8ef1265 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java @@ -30,45 +30,62 @@ import com.intellij.debugger.engine.requests.RequestManagerImpl; 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.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.JDOMExternalizerUtil; import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.WriteExternalException; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; import com.intellij.ui.AppUIUtil; +import com.intellij.ui.classFilter.ClassFilter; import com.intellij.util.StringBuilderSpinAllocator; -import com.sun.jdi.ObjectReference; -import com.sun.jdi.ReferenceType; -import com.sun.jdi.Value; -import com.sun.jdi.VoidValue; +import com.intellij.xdebugger.breakpoints.SuspendPolicy; +import com.intellij.xdebugger.breakpoints.XBreakpoint; +import com.intellij.xdebugger.breakpoints.XLineBreakpoint; +import com.sun.jdi.*; import com.sun.jdi.event.LocatableEvent; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.java.debugger.breakpoints.properties.JavaBreakpointProperties; import javax.swing.*; import java.util.List; -public abstract class Breakpoint extends FilteredRequestor implements ClassPrepareRequestor { - public boolean ENABLED = true; - public boolean LOG_ENABLED = false; - public boolean LOG_EXPRESSION_ENABLED = false; - public boolean REMOVE_AFTER_HIT = false; - private TextWithImports myLogMessage; // an expression to be evaluated and printed +public abstract class Breakpoint<P extends JavaBreakpointProperties> implements FilteredRequestor, ClassPrepareRequestor { + final XBreakpoint<P> myXBreakpoint; + protected final Project myProject; + + //private boolean ENABLED = true; + //private boolean LOG_ENABLED = false; + //private boolean LOG_EXPRESSION_ENABLED = false; + //private boolean REMOVE_AFTER_HIT = false; + //private TextWithImports myLogMessage; // an expression to be evaluated and printed @NonNls private static final String LOG_MESSAGE_OPTION_NAME = "LOG_MESSAGE"; public static final Breakpoint[] EMPTY_ARRAY = new Breakpoint[0]; protected boolean myCachedVerifiedState = false; + //private TextWithImportsImpl myLogMessage; - protected Breakpoint(@NotNull Project project) { - super(project); - myLogMessage = new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, ""); + protected Breakpoint(@NotNull Project project, XBreakpoint<P> xBreakpoint) { + //super(project); + myProject = project; + myXBreakpoint = xBreakpoint; + //myLogMessage = new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, ""); //noinspection AbstractMethodCallInConstructor - final BreakpointDefaults defaults = DebuggerManagerEx.getInstanceEx(project).getBreakpointManager().getBreakpointDefaults(getCategory()); - SUSPEND_POLICY = defaults.getSuspendPolicy(); - CONDITION_ENABLED = defaults.isConditionEnabled(); + //final BreakpointDefaults defaults = DebuggerManagerEx.getInstanceEx(project).getBreakpointManager().getBreakpointDefaults(getCategory()); + //SUSPEND_POLICY = defaults.getSuspendPolicy(); + //CONDITION_ENABLED = defaults.isConditionEnabled(); + } + + public Project getProject() { + return myProject; + } + + protected P getProperties() { + return myXBreakpoint.getProperties(); } public abstract PsiClass getPsiClass(); @@ -100,6 +117,16 @@ public abstract class Breakpoint extends FilteredRequestor implements ClassPrepa myCachedVerifiedState = isVerified; } + public boolean isRemoveAfterHit() { + return myXBreakpoint instanceof XLineBreakpoint && ((XLineBreakpoint)myXBreakpoint).isTemporary(); + } + + public void setRemoveAfterHit(boolean value) { + if (myXBreakpoint instanceof XLineBreakpoint) { + ((XLineBreakpoint)myXBreakpoint).setTemporary(value); + } + } + @Nullable public String getShortClassName() { final String className = getClassName(); @@ -210,19 +237,19 @@ public abstract class Breakpoint extends FilteredRequestor implements ClassPrepa private void runAction(final EvaluationContextImpl context, LocatableEvent event) { final DebugProcessImpl debugProcess = context.getDebugProcess(); - if (LOG_ENABLED || LOG_EXPRESSION_ENABLED) { + if (isLogEnabled() || isLogExpressionEnabled()) { final StringBuilder buf = StringBuilderSpinAllocator.alloc(); try { - if (LOG_ENABLED) { + if (myXBreakpoint.isLogMessage()) { buf.append(getEventMessage(event)); buf.append("\n"); } - final TextWithImports expressionToEvaluate = getLogMessage(); - if (LOG_EXPRESSION_ENABLED && expressionToEvaluate != null && !"".equals(expressionToEvaluate.getText())) { + if (isLogExpressionEnabled()) { if(!debugProcess.isAttached()) { return; } - + + final TextWithImports expressionToEvaluate = getLogMessage(); try { ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(getProject(), new EvaluatingComputable<ExpressionEvaluator>() { @Override @@ -252,11 +279,112 @@ public abstract class Breakpoint extends FilteredRequestor implements ClassPrepa StringBuilderSpinAllocator.dispose(buf); } } - if (REMOVE_AFTER_HIT) { + if (isRemoveAfterHit()) { handleTemporaryBreakpointHit(debugProcess); } } + /** + * @return true if the ID was added or false otherwise + */ + private boolean hasObjectID(long id) { + for (InstanceFilter instanceFilter : getInstanceFilters()) { + if (instanceFilter.getId() == id) { + return true; + } + } + return false; + } + + public boolean evaluateCondition(final EvaluationContextImpl context, LocatableEvent event) throws EvaluateException { + if(isCountFilterEnabled()) { + final DebugProcessImpl debugProcess = context.getDebugProcess(); + debugProcess.getVirtualMachineProxy().suspend(); + debugProcess.getRequestsManager().deleteRequest(this); + ((Breakpoint)this).createRequest(debugProcess); + debugProcess.getVirtualMachineProxy().resume(); + } + if (isInstanceFiltersEnabled()) { + Value value = context.getThisObject(); + if (value != null) { // non-static + ObjectReference reference = (ObjectReference)value; + if(!hasObjectID(reference.uniqueID())) { + return false; + } + } + } + + if (isClassFiltersEnabled()) { + String typeName = calculateEventClass(context, event); + if (!typeMatchesClassFilters(typeName)) return false; + } + + if (isConditionEnabled() && !getCondition().getText().isEmpty()) { + try { + ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(context.getProject(), new EvaluatingComputable<ExpressionEvaluator>() { + public ExpressionEvaluator compute() throws EvaluateException { + final SourcePosition contextSourcePosition = ContextUtil.getSourcePosition(context); + // IMPORTANT: calculate context psi element basing on the location where the exception + // has been hit, not on the location where it was set. (For line breakpoints these locations are the same, however, + // for method, exception and field breakpoints these locations differ) + PsiElement contextPsiElement = ContextUtil.getContextElement(contextSourcePosition); + if (contextPsiElement == null) { + contextPsiElement = getEvaluationElement(); // as a last resort + } + return EvaluatorBuilderImpl.build(getCondition(), contextPsiElement, contextSourcePosition); + } + }); + final Value value = evaluator.evaluate(context); + if (!(value instanceof BooleanValue)) { + throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.boolean.expected")); + } + if(!((BooleanValue)value).booleanValue()) { + return false; + } + } + catch (EvaluateException ex) { + if(ex.getCause() instanceof VMDisconnectedException) { + return false; + } + throw EvaluateExceptionUtil.createEvaluateException( + DebuggerBundle.message("error.failed.evaluating.breakpoint.condition", getCondition(), ex.getMessage()) + ); + } + return true; + } + + return true; + } + + protected String calculateEventClass(EvaluationContextImpl context, LocatableEvent event) throws EvaluateException { + return event.location().declaringType().name(); + } + + private boolean typeMatchesClassFilters(@Nullable String typeName) { + if (typeName == null) { + return true; + } + boolean matches = false, hasEnabled = false; + for (ClassFilter classFilter : getClassFilters()) { + if (classFilter.isEnabled()) { + hasEnabled = true; + if (classFilter.matches(typeName)) { + matches = true; + break; + } + } + } + if(hasEnabled && !matches) { + return false; + } + for (ClassFilter classFilter : getClassExclusionFilters()) { + if (classFilter.isEnabled() && classFilter.matches(typeName)) { + return false; + } + } + return true; + } + private void handleTemporaryBreakpointHit(final DebugProcessImpl debugProcess) { debugProcess.addDebugProcessListener(new DebugProcessAdapter() { @Override @@ -288,26 +416,213 @@ public abstract class Breakpoint extends FilteredRequestor implements ClassPrepa RequestManagerImpl.deleteRequests(this); } - @Override public void readExternal(Element parentNode) throws InvalidDataException { - super.readExternal(parentNode); - String logMessage = JDOMExternalizerUtil.readField(parentNode, LOG_MESSAGE_OPTION_NAME); - if (logMessage != null) { - setLogMessage(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, logMessage)); + FilteredRequestorImpl requestor = new FilteredRequestorImpl(myProject); + requestor.readTo(parentNode, this); + try { + setEnabled(Boolean.valueOf(JDOMExternalizerUtil.readField(parentNode, "ENABLED"))); + } catch (Exception e) { } + try { + setLogEnabled(Boolean.valueOf(JDOMExternalizerUtil.readField(parentNode, "LOG_ENABLED"))); + } catch (Exception e) { + } + try { + if (Boolean.valueOf(JDOMExternalizerUtil.readField(parentNode, "LOG_EXPRESSION_ENABLED"))) { + String logMessage = JDOMExternalizerUtil.readField(parentNode, LOG_MESSAGE_OPTION_NAME); + if (logMessage != null) { + setLogMessage(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, logMessage)); + } + } + } catch (Exception e) { + } + try { + setRemoveAfterHit(Boolean.valueOf(JDOMExternalizerUtil.readField(parentNode, "REMOVE_AFTER_HIT"))); + } catch (Exception e) { + } + } + + //@Override + //public void writeExternal(Element parentNode) throws WriteExternalException { + //super.writeExternal(parentNode); + //JDOMExternalizerUtil.writeField(parentNode, LOG_MESSAGE_OPTION_NAME, getLogMessage().toExternalForm()); + //} + + //public void setLogMessage(TextWithImports logMessage) { + // myLogMessage = logMessage; + //} + + public abstract PsiElement getEvaluationElement(); + + protected TextWithImports getLogMessage() { + return new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, myXBreakpoint.getLogExpression()); + } + + protected TextWithImports getCondition() { + return new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, myXBreakpoint.getCondition()); + } + + public boolean isEnabled() { + return myXBreakpoint.isEnabled(); + } + + public void setEnabled(boolean enabled) { + myXBreakpoint.setEnabled(enabled); + } + + protected boolean isLogEnabled() { + return myXBreakpoint.isLogMessage(); + } + + public void setLogEnabled(boolean logEnabled) { + myXBreakpoint.setLogMessage(logEnabled); + } + + protected boolean isLogExpressionEnabled() { + String expression = myXBreakpoint.getLogExpression(); + if (expression == null || expression.isEmpty()) { + return false; + } + return !getLogMessage().isEmpty(); + } + + @Override + public boolean isCountFilterEnabled() { + if (getProperties() == null) { + return false; + } + return getProperties().COUNT_FILTER_ENABLED; + } + public void setCountFilterEnabled(boolean enabled) { + getProperties().COUNT_FILTER_ENABLED = enabled; + } + + @Override + public int getCountFilter() { + return getProperties().COUNT_FILTER; + } + + public void setCountFilter(int filter) { + getProperties().COUNT_FILTER = filter; + } + + @Override + public boolean isClassFiltersEnabled() { + if (getProperties() == null) { + return false; + } + return getProperties().CLASS_FILTERS_ENABLED; + } + + public void setClassFiltersEnabled(boolean enabled) { + getProperties().CLASS_FILTERS_ENABLED = enabled; + } + + @Override + public ClassFilter[] getClassFilters() { + return getProperties().getClassFilters(); + } + + public void setClassFilters(ClassFilter[] filters) { + getProperties().setClassFilters(filters); + } + + @Override + public ClassFilter[] getClassExclusionFilters() { + return getProperties().getClassExclusionFilters(); + } + + protected void setClassExclusionFilters(ClassFilter[] filters) { + getProperties().setClassExclusionFilters(filters); + } + + @Override + public boolean isInstanceFiltersEnabled() { + if (getProperties() == null) { + return false; + } + return getProperties().INSTANCE_FILTERS_ENABLED; + } + + public void setInstanceFiltersEnabled(boolean enabled) { + getProperties().INSTANCE_FILTERS_ENABLED = enabled; } @Override - public void writeExternal(Element parentNode) throws WriteExternalException { - super.writeExternal(parentNode); - JDOMExternalizerUtil.writeField(parentNode, LOG_MESSAGE_OPTION_NAME, getLogMessage().toExternalForm()); + public InstanceFilter[] getInstanceFilters() { + return getProperties().getInstanceFilters(); + } + + public void setInstanceFilters(InstanceFilter[] filters) { + getProperties().setInstanceFilters(filters); + } + + private static String getSuspendPolicy(XBreakpoint breakpoint) { + switch (breakpoint.getSuspendPolicy()) { + case ALL: + return DebuggerSettings.SUSPEND_ALL; + case THREAD: + return DebuggerSettings.SUSPEND_THREAD; + case NONE: + return DebuggerSettings.SUSPEND_NONE; + + default: + throw new IllegalArgumentException("unknown suspend policy"); + } + } + + static SuspendPolicy transformSuspendPolicy(String policy) { + if (DebuggerSettings.SUSPEND_ALL.equals(policy)) { + return SuspendPolicy.ALL; + } else if (DebuggerSettings.SUSPEND_THREAD.equals(policy)) { + return SuspendPolicy.THREAD; + } else if (DebuggerSettings.SUSPEND_NONE.equals(policy)) { + return SuspendPolicy.NONE; + } else { + throw new IllegalArgumentException("unknown suspend policy"); + } } - public TextWithImports getLogMessage() { - return myLogMessage; + protected boolean isSuspend() { + return myXBreakpoint.getSuspendPolicy() != SuspendPolicy.NONE; + } + + @Override + public String getSuspendPolicy() { + return getSuspendPolicy(myXBreakpoint); + } + + public void setSuspendPolicy(String policy) { + myXBreakpoint.setSuspendPolicy(transformSuspendPolicy(policy)); + } + + protected void setLogMessage(@Nullable TextWithImports logMessage) { + if (logMessage != null && !logMessage.getText().isEmpty()) { + myXBreakpoint.setLogExpression(logMessage.toExternalForm()); + } + else { + myXBreakpoint.setLogExpression(null); + } + } + + protected boolean isConditionEnabled() { + String condition = myXBreakpoint.getCondition(); + if (condition == null || condition.isEmpty()) { + return false; + } + return !getCondition().isEmpty(); + } + + public void setCondition(@Nullable TextWithImports condition) { + if (condition != null && !condition.getText().isEmpty()) { + myXBreakpoint.setCondition(condition.toExternalForm()); + } + else { + myXBreakpoint.setCondition(null); + } } - public void setLogMessage(TextWithImports logMessage) { - myLogMessage = logMessage; + protected void addInstanceFilter(long l) { + getProperties().addInstanceFilter(l); } } |