@@ -143,6 +143,15 @@ public enum Kind {
143143 static final Kind [] VALUES = values ();
144144 }
145145
146+ /**
147+ * A simple way to have conditional breakpoints, without language-level expressions.
148+ *
149+ * This is meant to implement complex breakpoints for the debugger.
150+ */
151+ public interface SimpleCondition {
152+ boolean evaluate ();
153+ }
154+
146155 private static final Breakpoint BUILDER_INSTANCE = new Breakpoint ();
147156
148157 private final SuspendAnchor suspendAnchor ;
@@ -159,10 +168,13 @@ public enum Kind {
159168 private volatile boolean resolved ;
160169 private volatile int ignoreCount ;
161170 private volatile boolean disposed ;
171+
162172 private volatile String condition ;
163173 private volatile boolean global ;
164174 private volatile GlobalBreakpoint roWrapper ;
165175
176+ private volatile SimpleCondition simpleCondition ;
177+
166178 /* We use long instead of int in the implementation to avoid not hitting again on overflows. */
167179 private final AtomicLong hitCount = new AtomicLong ();
168180 private volatile Assumption conditionUnchanged ;
@@ -294,8 +306,18 @@ public boolean isResolved() {
294306 * @since 0.9
295307 */
296308 public synchronized void setCondition (String expression ) {
297- boolean existsChanged = (this .condition == null ) != (expression == null );
309+ boolean existsChanged = conditionIsNotSet () != (expression == null );
310+
298311 this .condition = expression ;
312+ this .simpleCondition = null ;
313+ invalidateConditionUnchangedAssumption (existsChanged );
314+ }
315+
316+ private boolean conditionIsNotSet () {
317+ return this .condition == null && this .simpleCondition == null ;
318+ }
319+
320+ private void invalidateConditionUnchangedAssumption (boolean existsChanged ) {
299321 Assumption assumption = conditionUnchanged ;
300322 if (assumption != null ) {
301323 this .conditionUnchanged = null ;
@@ -310,6 +332,13 @@ public synchronized void setCondition(String expression) {
310332 }
311333 }
312334
335+ public synchronized void setCondition (SimpleCondition condition ) {
336+ boolean existsChanged = conditionIsNotSet () != (condition == null );
337+ this .simpleCondition = condition ;
338+ this .condition = null ;
339+ invalidateConditionUnchangedAssumption (existsChanged );
340+ }
341+
313342 /**
314343 * Returns the expression used to create the current breakpoint condition, null if no condition
315344 * set.
@@ -1162,7 +1191,7 @@ private abstract static class AbstractBreakpointNode extends DebuggerNode {
11621191 private final Breakpoint breakpoint ;
11631192 protected final BranchProfile breakBranch = BranchProfile .create ();
11641193
1165- @ Child private ConditionalBreakNode breakCondition ;
1194+ @ Child private AbstractConditionalBreakNode breakCondition ;
11661195 @ CompilationFinal private Assumption conditionExistsUnchanged ;
11671196 @ CompilationFinal (dimensions = 1 ) private DebuggerSession [] sessions ;
11681197 @ CompilationFinal private Assumption sessionsUnchanged ;
@@ -1173,8 +1202,13 @@ private abstract static class AbstractBreakpointNode extends DebuggerNode {
11731202 initializeSessions ();
11741203 this .conditionExistsUnchanged = breakpoint .getConditionExistsUnchanged ();
11751204 if (breakpoint .condition != null ) {
1205+ assert breakpoint .simpleCondition == null : "We don't support both conditions being set at the same time." ;
11761206 this .breakCondition = new ConditionalBreakNode (context , breakpoint );
11771207 }
1208+ if (breakpoint .simpleCondition != null ) {
1209+ assert breakpoint .condition == null : "We don't support both conditions being set at the same time." ;
1210+ this .breakCondition = new SimpleConditionalBreakNode (context , breakpoint );
1211+ }
11781212 }
11791213
11801214 private void initializeSessions () {
@@ -1275,7 +1309,7 @@ boolean testCondition(VirtualFrame frame) throws BreakpointConditionFailure {
12751309 }
12761310 if (breakCondition != null ) {
12771311 try {
1278- return breakCondition .executeBreakCondition (frame , sessions );
1312+ return breakCondition .shouldBreak (frame , sessions );
12791313 } catch (Throwable e ) {
12801314 CompilerDirectives .transferToInterpreter ();
12811315 throw new BreakpointConditionFailure (breakpoint , e );
@@ -1328,24 +1362,56 @@ public Throwable getConditionFailure() {
13281362
13291363 }
13301364
1331- private static class ConditionalBreakNode extends Node {
1365+ private abstract static class AbstractConditionalBreakNode extends Node {
1366+
1367+ protected final EventContext context ;
1368+ protected final Breakpoint breakpoint ;
1369+
1370+ @ Child protected SetThreadSuspensionEnabledNode suspensionEnabledNode = SetThreadSuspensionEnabledNodeGen .create ();
1371+
1372+ @ CompilationFinal protected Assumption conditionUnchanged ;
1373+
1374+ AbstractConditionalBreakNode (EventContext context , Breakpoint breakpoint ) {
1375+ this .context = context ;
1376+ this .breakpoint = breakpoint ;
1377+ this .conditionUnchanged = breakpoint .getConditionUnchanged ();
1378+ }
1379+
1380+ abstract boolean shouldBreak (VirtualFrame frame , DebuggerSession [] sessions );
1381+ }
1382+
1383+ private static class SimpleConditionalBreakNode extends AbstractConditionalBreakNode {
1384+ @ CompilationFinal private SimpleCondition condition ;
1385+
1386+ SimpleConditionalBreakNode (EventContext context , Breakpoint breakpoint ) {
1387+ super (context , breakpoint );
1388+ this .condition = breakpoint .simpleCondition ;
1389+ }
1390+
1391+ @ Override
1392+ boolean shouldBreak (VirtualFrame frame , DebuggerSession [] sessions ) {
1393+ if (!conditionUnchanged .isValid ()) {
1394+ CompilerDirectives .transferToInterpreterAndInvalidate ();
1395+ condition = breakpoint .simpleCondition ;
1396+ }
1397+
1398+ return condition .evaluate ();
1399+ }
1400+ }
1401+
1402+ private static class ConditionalBreakNode extends AbstractConditionalBreakNode {
13321403
13331404 private static final Object [] EMPTY_ARRAY = new Object [0 ];
13341405
1335- private final EventContext context ;
1336- private final Breakpoint breakpoint ;
1337- @ Child private SetThreadSuspensionEnabledNode suspensionEnabledNode = SetThreadSuspensionEnabledNodeGen .create ();
13381406 @ Child private DirectCallNode conditionCallNode ;
13391407 @ Child private ExecutableNode conditionSnippet ;
1340- @ CompilationFinal private Assumption conditionUnchanged ;
13411408
13421409 ConditionalBreakNode (EventContext context , Breakpoint breakpoint ) {
1343- this .context = context ;
1344- this .breakpoint = breakpoint ;
1345- this .conditionUnchanged = breakpoint .getConditionUnchanged ();
1410+ super (context , breakpoint );
13461411 }
13471412
1348- boolean executeBreakCondition (VirtualFrame frame , DebuggerSession [] sessions ) {
1413+ @ Override
1414+ boolean shouldBreak (VirtualFrame frame , DebuggerSession [] sessions ) {
13491415 if ((conditionSnippet == null && conditionCallNode == null ) || !conditionUnchanged .isValid ()) {
13501416 CompilerDirectives .transferToInterpreterAndInvalidate ();
13511417 initializeConditional (frame .materialize ());
0 commit comments