diff --git a/Sindarin-Tests/SindarinDebuggerTest.class.st b/Sindarin-Tests/SindarinDebuggerTest.class.st index a272616..dd2d992 100644 --- a/Sindarin-Tests/SindarinDebuggerTest.class.st +++ b/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 [ @@ -1279,6 +1291,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 context 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 context instructionStream willReturn +] + { #category : #tests } SindarinDebuggerTest >> testSkipDoesNotSkipReturn [ diff --git a/Sindarin/SindarinDebugger.class.st b/Sindarin/SindarinDebugger.class.st index ce84b83..50d7573 100644 --- a/Sindarin/SindarinDebugger.class.st +++ b/Sindarin/SindarinDebugger.class.st @@ -686,7 +686,25 @@ SindarinDebugger >> skipMessageNodeWith: replacementValue [ { #category : #'stepping - skip' } SindarinDebugger >> skipReturnNode [ - ^ SindarinSkippingReturnWarning signal: 'Cannot skip a return node' + | node allReturnNodes currentBytecode | + 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' ]. + + currentBytecode := self nextBytecode. + self context pc: self context pc + currentBytecode bytes size. + self debugSession stepToFirstInterestingBytecodeWithJumpIn: + self debugSession interruptedProcess ] { #category : #'stepping - skip' }