forked from go-delve/delve
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
proc: initial stepping with range-over-func support
Initial support for stepping in functions that use the new range-over-func statement in go1.23. Does not support: - inlining - viewing variables of the enclosing function from a range-over-func body closure - the correct way to find the enclosing function from a range-over-func body closure (but it should work most of the time). Updates go-delve#3733
- Loading branch information
Showing
6 changed files
with
1,204 additions
and
156 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,328 @@ | ||
package main | ||
|
||
// The tests here are adapted from $GOROOT/src/cmd/compile/internal/rangefunc/rangefunc_test.go | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
/* | ||
THESE | ||
LINES | ||
INTENTIONALLY | ||
LEFT | ||
BLANK | ||
*/ | ||
|
||
func TestTrickyIterAll() { | ||
trickItAll := TrickyIterator{} | ||
i := 0 | ||
for _, x := range trickItAll.iterAll([]int{30, 7, 8, 9, 10}) { | ||
i += x | ||
if i >= 36 { | ||
break | ||
} | ||
} | ||
|
||
fmt.Println("Got i = ", i) | ||
} | ||
|
||
func TestTrickyIterAll2() { | ||
trickItAll := TrickyIterator{} | ||
i := 0 | ||
for _, x := range trickItAll.iterAll([]int{42, 47}) { | ||
i += x | ||
} | ||
fmt.Println(i) | ||
} | ||
|
||
func TestBreak1() { | ||
var result []int | ||
for _, x := range OfSliceIndex([]int{-1, -2, -4}) { | ||
if x == -4 { | ||
break | ||
} | ||
for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { | ||
if y == 3 { | ||
break | ||
} | ||
result = append(result, y) | ||
} | ||
result = append(result, x) | ||
} | ||
fmt.Println(result) | ||
} | ||
|
||
func TestBreak2() { | ||
var result []int | ||
outer: | ||
for _, x := range OfSliceIndex([]int{-1, -2, -4}) { | ||
for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { | ||
if y == 3 { | ||
break | ||
} | ||
if x == -4 { | ||
break outer | ||
} | ||
result = append(result, y) | ||
} | ||
result = append(result, x) | ||
} | ||
fmt.Println(result) | ||
} | ||
|
||
func TestMultiCont0() { | ||
var result []int | ||
|
||
W: | ||
for _, w := range OfSliceIndex([]int{1000, 2000}) { | ||
result = append(result, w) | ||
if w == 2000 { | ||
break | ||
} | ||
for _, x := range OfSliceIndex([]int{100, 200, 300, 400}) { | ||
for _, y := range OfSliceIndex([]int{10, 20, 30, 40}) { | ||
result = append(result, y) | ||
for _, z := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { | ||
if z&1 == 1 { | ||
continue | ||
} | ||
result = append(result, z) | ||
if z >= 4 { | ||
continue W // modified to be multilevel | ||
} | ||
} | ||
result = append(result, -y) // should never be executed | ||
} | ||
result = append(result, x) | ||
} | ||
} | ||
fmt.Println(result) | ||
} | ||
|
||
func TestPanickyIterator1() { | ||
var result []int | ||
defer func() { | ||
r := recover() | ||
fmt.Println("Recovering", r) | ||
}() | ||
for _, z := range PanickyOfSliceIndex([]int{1, 2, 3, 4}) { | ||
result = append(result, z) | ||
if z == 4 { | ||
break | ||
} | ||
} | ||
fmt.Println(result) | ||
} | ||
|
||
func TestPanickyIterator2() { | ||
var result []int | ||
defer func() { | ||
r := recover() | ||
fmt.Println("Recovering ", r) | ||
}() | ||
for _, x := range OfSliceIndex([]int{100, 200}) { | ||
result = append(result, x) | ||
Y: | ||
// swallows panics and iterates to end BUT `break Y` disables the body, so--> 10, 1, 2 | ||
for _, y := range VeryBadOfSliceIndex([]int{10, 20}) { | ||
result = append(result, y) | ||
|
||
// converts early exit into a panic --> 1, 2 | ||
for k, z := range PanickyOfSliceIndex([]int{1, 2}) { // iterator panics | ||
result = append(result, z) | ||
if k == 1 { | ||
break Y | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
func TestPanickyIteratorWithNewDefer() { | ||
var result []int | ||
defer func() { | ||
r := recover() | ||
fmt.Println("Recovering ", r) | ||
}() | ||
for _, x := range OfSliceIndex([]int{100, 200}) { | ||
result = append(result, x) | ||
Y: | ||
// swallows panics and iterates to end BUT `break Y` disables the body, so--> 10, 1, 2 | ||
for _, y := range VeryBadOfSliceIndex([]int{10, 20}) { | ||
defer func() { // This defer will be set on TestPanickyIteratorWithNewDefer from TestPanickyIteratorWithNewDefer-range2 | ||
fmt.Println("y loop defer") | ||
}() | ||
result = append(result, y) | ||
|
||
// converts early exit into a panic --> 1, 2 | ||
for k, z := range PanickyOfSliceIndex([]int{1, 2}) { // iterator panics | ||
result = append(result, z) | ||
if k == 1 { | ||
break Y | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
func TestLongReturnWrapper() { | ||
TestLongReturn() | ||
fmt.Println("returned") | ||
} | ||
|
||
func TestLongReturn() { | ||
for _, x := range OfSliceIndex([]int{1, 2, 3}) { | ||
for _, y := range OfSliceIndex([]int{10, 20, 30}) { | ||
if y == 10 { | ||
return | ||
} | ||
} | ||
fmt.Println(x) | ||
} | ||
} | ||
|
||
func TestGotoA1() { | ||
result := []int{} | ||
for _, x := range OfSliceIndex([]int{-1, -4, -5}) { | ||
result = append(result, x) | ||
if x == -4 { | ||
break | ||
} | ||
for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { | ||
if y == 3 { | ||
goto A | ||
} | ||
result = append(result, y) | ||
} | ||
A: | ||
result = append(result, x) | ||
} | ||
fmt.Println(result) | ||
} | ||
|
||
func TestGotoB1() { | ||
result := []int{} | ||
for _, x := range OfSliceIndex([]int{-1, -2, -3, -4, -5}) { | ||
result = append(result, x) | ||
if x == -4 { | ||
break | ||
} | ||
for _, y := range OfSliceIndex([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { | ||
if y == 3 { | ||
goto B | ||
} | ||
result = append(result, y) | ||
} | ||
result = append(result, x) | ||
} | ||
B: | ||
result = append(result, 999) | ||
fmt.Println(result) | ||
} | ||
|
||
func main() { | ||
TestTrickyIterAll() | ||
TestTrickyIterAll2() | ||
TestBreak1() | ||
TestBreak2() | ||
TestMultiCont0() | ||
TestPanickyIterator1() | ||
TestPanickyIterator2() | ||
TestPanickyIteratorWithNewDefer() | ||
TestLongReturnWrapper() | ||
TestGotoA1() | ||
TestGotoB1() | ||
} | ||
|
||
type Seq[T any] func(yield func(T) bool) | ||
type Seq2[T1, T2 any] func(yield func(T1, T2) bool) | ||
|
||
type TrickyIterator struct { | ||
yield func(int, int) bool | ||
} | ||
|
||
func (ti *TrickyIterator) iterEcho(s []int) Seq2[int, int] { | ||
return func(yield func(int, int) bool) { | ||
for i, v := range s { | ||
if !yield(i, v) { | ||
ti.yield = yield | ||
return | ||
} | ||
if ti.yield != nil && !ti.yield(i, v) { | ||
return | ||
} | ||
} | ||
ti.yield = yield | ||
return | ||
} | ||
} | ||
|
||
func (ti *TrickyIterator) iterAll(s []int) Seq2[int, int] { | ||
return func(yield func(int, int) bool) { | ||
ti.yield = yield // Save yield for future abuse | ||
for i, v := range s { | ||
if !yield(i, v) { | ||
return | ||
} | ||
} | ||
return | ||
} | ||
} | ||
|
||
func (ti *TrickyIterator) iterZero(s []int) Seq2[int, int] { | ||
return func(yield func(int, int) bool) { | ||
ti.yield = yield // Save yield for future abuse | ||
// Don't call it at all, maybe it won't escape | ||
return | ||
} | ||
} | ||
|
||
// OfSliceIndex returns a Seq2 over the elements of s. It is equivalent | ||
// to range s. | ||
func OfSliceIndex[T any, S ~[]T](s S) Seq2[int, T] { | ||
return func(yield func(int, T) bool) { | ||
for i, v := range s { | ||
if !yield(i, v) { | ||
return | ||
} | ||
} | ||
return | ||
} | ||
} | ||
|
||
// PanickyOfSliceIndex iterates the slice but panics if it exits the loop early | ||
func PanickyOfSliceIndex[T any, S ~[]T](s S) Seq2[int, T] { | ||
return func(yield func(int, T) bool) { | ||
for i, v := range s { | ||
if !yield(i, v) { | ||
panic(fmt.Errorf("Panicky iterator panicking")) | ||
} | ||
} | ||
return | ||
} | ||
} | ||
|
||
// VeryBadOfSliceIndex is "very bad" because it ignores the return value from yield | ||
// and just keeps on iterating, and also wraps that call in a defer-recover so it can | ||
// keep on trying after the first panic. | ||
func VeryBadOfSliceIndex[T any, S ~[]T](s S) Seq2[int, T] { | ||
return func(yield func(int, T) bool) { | ||
for i, v := range s { | ||
func() { | ||
defer func() { | ||
recover() | ||
}() | ||
yield(i, v) | ||
}() | ||
} | ||
return | ||
} | ||
} |
Oops, something went wrong.