diff --git a/src/Sindarin-Tests/SindarinDebuggerTest.class.st b/src/Sindarin-Tests/SindarinDebuggerTest.class.st index 4dd99e5..d89c2aa 100644 --- a/src/Sindarin-Tests/SindarinDebuggerTest.class.st +++ b/src/Sindarin-Tests/SindarinDebuggerTest.class.st @@ -145,6 +145,18 @@ SindarinDebuggerTest >> methodWithSeveralInstructionsInBlock [ ^ 42 ] +{ #category : #helpers } +SindarinDebuggerTest >> methodWithSeveralReturns [ + "There is only one explicit return but there is also an implicit return after `a := 3`. In that sense, there are several returns in this method" + + | a | + a := true. + a + ifFalse: [ ^ a := 1 ] + ifTrue: [ a := 2 ]. + a := 3 +] + { #category : #helpers } SindarinDebuggerTest >> methodWithTwoAssignments [ @@ -1311,6 +1323,52 @@ SindarinDebuggerTest >> testSkipBlockNode [ self assert: scdbg topStack equals: 43 ] +{ #category : #tests } +SindarinDebuggerTest >> testSkipCanSkipReturnIfItIsNotTheLastReturn [ + + | scdbg | + scdbg := SindarinDebugger debug: [ self methodWithSeveralReturns ]. + + "we step until we arrive on the node `^ a := 1` in `SindarinDebuggerTest>>#methodWithSeveralReturns`." + scdbg + step; + skip; + skip; + step. + + self assert: scdbg node isReturn. + self assert: scdbg topStack equals: 1. + + "We skip the return node" + self shouldnt: [ scdbg skip ] raise: SindarinSkippingReturnWarning. + + "We should be on the `a := 2` node" + self assert: scdbg node isAssignment. + self assert: scdbg node value value equals: 2 +] + +{ #category : #tests } +SindarinDebuggerTest >> testSkipCannotSkipReturnIfItIsTheLastReturn [ + + | scdbg nodeWithImplicitReturn | + scdbg := SindarinDebugger debug: [ self methodWithSeveralReturns ]. + + "we step until we arrive on the method node in `SindarinDebuggerTest>>#methodWithSeveralReturns`, which is mapped to the implicit return bycode." + scdbg step. + nodeWithImplicitReturn := scdbg methodNode. + 3 timesRepeat: [ scdbg step ]. + + self assert: scdbg node identicalTo: nodeWithImplicitReturn. + self assert: scdbg instructionStream willReturn. + + "We skip the return node" + self should: [ scdbg skip ] raise: SindarinSkippingReturnWarning. + + "We should still be on the method node" + self assert: scdbg node identicalTo: nodeWithImplicitReturn. + self assert: scdbg instructionStream willReturn +] + { #category : #tests } SindarinDebuggerTest >> testSkipDoesNotSkipReturn [ diff --git a/src/Sindarin/SindarinDebugger.class.st b/src/Sindarin/SindarinDebugger.class.st index cddd0a4..afa6a32 100644 --- a/src/Sindarin/SindarinDebugger.class.st +++ b/src/Sindarin/SindarinDebugger.class.st @@ -424,8 +424,24 @@ SindarinDebugger >> skipMessageNodeWith: replacementValue [ { #category : #'stepping - skip' } SindarinDebugger >> skipReturnNode [ - self flag: 'We should be able to skip a return node as long as it''s not the last one in the method'. - ^ SindarinSkippingReturnWarning signal: 'Cannot skip a return node' + | node allReturnNodes | + node := self node. + + "We collect the list of nodes associated to a return bytecode, via the IR" + allReturnNodes := self method ir children flatCollect: [ :irSequence | + irSequence sequence + select: [ :irInstruction | + irInstruction isReturn ] + thenCollect: [ :irInstruction | + irInstruction sourceNode ] ]. + "If this is the last node of the method that is mapped to a return bytecode, we can't skip it and we stop there." + node == allReturnNodes last ifTrue: [ + ^ SindarinSkippingReturnWarning signal: + 'Cannot skip the last return in the method' ]. + + self skipPcToNextBytecode. + self debugSession stepToFirstInterestingBytecodeWithJumpIn: + self debugSession interruptedProcess ] { #category : #'stepping - skip' }