summaryrefslogtreecommitdiffstats
path: root/test/recover.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:14:23 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:14:23 +0000
commit73df946d56c74384511a194dd01dbe099584fd1a (patch)
treefd0bcea490dd81327ddfbb31e215439672c9a068 /test/recover.go
parentInitial commit. (diff)
downloadgolang-1.16-73df946d56c74384511a194dd01dbe099584fd1a.tar.xz
golang-1.16-73df946d56c74384511a194dd01dbe099584fd1a.zip
Adding upstream version 1.16.10.upstream/1.16.10upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'test/recover.go')
-rw-r--r--test/recover.go587
1 files changed, 587 insertions, 0 deletions
diff --git a/test/recover.go b/test/recover.go
new file mode 100644
index 0000000..e4187c0
--- /dev/null
+++ b/test/recover.go
@@ -0,0 +1,587 @@
+// run
+
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test of basic recover functionality.
+
+package main
+
+import (
+ "os"
+ "reflect"
+ "runtime"
+)
+
+func main() {
+ // go.tools/ssa/interp still has:
+ // - some lesser bugs in recover()
+ // - incomplete support for reflection
+ interp := os.Getenv("GOSSAINTERP") != ""
+
+ test1()
+ test1WithClosures()
+ test2()
+ test3()
+ if !interp {
+ test4()
+ }
+ test5()
+ test6()
+ test6WithClosures()
+ test7()
+ test8()
+ test9()
+ if !interp {
+ test9reflect1()
+ test9reflect2()
+ }
+ test10()
+ if !interp {
+ test10reflect1()
+ test10reflect2()
+ }
+ test11()
+ if !interp {
+ test11reflect1()
+ test11reflect2()
+ }
+ test111()
+ test12()
+ if !interp {
+ test12reflect1()
+ test12reflect2()
+ }
+ test13()
+ if !interp {
+ test13reflect1()
+ test13reflect2()
+ }
+ test14()
+ if !interp {
+ test14reflect1()
+ test14reflect2()
+ test15()
+ test16()
+ }
+}
+
+func die() {
+ runtime.Breakpoint() // can't depend on panic
+}
+
+func mustRecoverBody(v1, v2, v3, x interface{}) {
+ v := v1
+ if v != nil {
+ println("spurious recover", v)
+ die()
+ }
+ v = v2
+ if v == nil {
+ println("missing recover", x.(int))
+ die() // panic is useless here
+ }
+ if v != x {
+ println("wrong value", v, x)
+ die()
+ }
+
+ // the value should be gone now regardless
+ v = v3
+ if v != nil {
+ println("recover didn't recover")
+ die()
+ }
+}
+
+func doubleRecover() interface{} {
+ return recover()
+}
+
+func mustRecover(x interface{}) {
+ mustRecoverBody(doubleRecover(), recover(), recover(), x)
+}
+
+func mustNotRecover() {
+ v := recover()
+ if v != nil {
+ println("spurious recover", v)
+ die()
+ }
+}
+
+func withoutRecover() {
+ mustNotRecover() // because it's a sub-call
+}
+
+func withoutRecoverRecursive(n int) {
+ if n == 0 {
+ withoutRecoverRecursive(1)
+ } else {
+ v := recover()
+ if v != nil {
+ println("spurious recover (recursive)", v)
+ die()
+ }
+ }
+}
+
+func test1() {
+ defer mustNotRecover() // because mustRecover will squelch it
+ defer mustRecover(1) // because of panic below
+ defer withoutRecover() // should be no-op, leaving for mustRecover to find
+ defer withoutRecoverRecursive(0) // ditto
+ panic(1)
+}
+
+// Repeat test1 with closures instead of standard function.
+// Interesting because recover bases its decision
+// on the frame pointer of its caller, and a closure's
+// frame pointer is in the middle of its actual arguments
+// (after the hidden ones for the closed-over variables).
+func test1WithClosures() {
+ defer func() {
+ v := recover()
+ if v != nil {
+ println("spurious recover in closure")
+ die()
+ }
+ }()
+ defer func(x interface{}) {
+ mustNotRecover()
+ v := recover()
+ if v == nil {
+ println("missing recover", x.(int))
+ die()
+ }
+ if v != x {
+ println("wrong value", v, x)
+ die()
+ }
+ }(1)
+ defer func() {
+ mustNotRecover()
+ }()
+ panic(1)
+}
+
+func test2() {
+ // Recover only sees the panic argument
+ // if it is called from a deferred call.
+ // It does not see the panic when called from a call within a deferred call (too late)
+ // nor does it see the panic when it *is* the deferred call (too early).
+ defer mustRecover(2)
+ defer recover() // should be no-op
+ panic(2)
+}
+
+func test3() {
+ defer mustNotRecover()
+ defer func() {
+ recover() // should squelch
+ }()
+ panic(3)
+}
+
+func test4() {
+ // Equivalent to test3 but using defer to make the call.
+ defer mustNotRecover()
+ defer func() {
+ defer recover() // should squelch
+ }()
+ panic(4)
+}
+
+// Check that closures can set output arguments.
+// Run g(). If it panics, return x; else return deflt.
+func try(g func(), deflt interface{}) (x interface{}) {
+ defer func() {
+ if v := recover(); v != nil {
+ x = v
+ }
+ }()
+ defer g()
+ return deflt
+}
+
+// Check that closures can set output arguments.
+// Run g(). If it panics, return x; else return deflt.
+func try1(g func(), deflt interface{}) (x interface{}) {
+ defer func() {
+ if v := recover(); v != nil {
+ x = v
+ }
+ }()
+ defer g()
+ x = deflt
+ return
+}
+
+func test5() {
+ v := try(func() { panic(5) }, 55).(int)
+ if v != 5 {
+ println("wrong value", v, 5)
+ die()
+ }
+
+ s := try(func() {}, "hi").(string)
+ if s != "hi" {
+ println("wrong value", s, "hi")
+ die()
+ }
+
+ v = try1(func() { panic(5) }, 55).(int)
+ if v != 5 {
+ println("try1 wrong value", v, 5)
+ die()
+ }
+
+ s = try1(func() {}, "hi").(string)
+ if s != "hi" {
+ println("try1 wrong value", s, "hi")
+ die()
+ }
+}
+
+// When a deferred big call starts, it must first
+// create yet another stack segment to hold the
+// giant frame for x. Make sure that doesn't
+// confuse recover.
+func big(mustRecover bool) {
+ var x [100000]int
+ x[0] = 1
+ x[99999] = 1
+ _ = x
+
+ v := recover()
+ if mustRecover {
+ if v == nil {
+ println("missing big recover")
+ die()
+ }
+ } else {
+ if v != nil {
+ println("spurious big recover")
+ die()
+ }
+ }
+}
+
+func test6() {
+ defer big(false)
+ defer big(true)
+ panic(6)
+}
+
+func test6WithClosures() {
+ defer func() {
+ var x [100000]int
+ x[0] = 1
+ x[99999] = 1
+ _ = x
+ if recover() != nil {
+ println("spurious big closure recover")
+ die()
+ }
+ }()
+ defer func() {
+ var x [100000]int
+ x[0] = 1
+ x[99999] = 1
+ _ = x
+ if recover() == nil {
+ println("missing big closure recover")
+ die()
+ }
+ }()
+ panic("6WithClosures")
+}
+
+func test7() {
+ ok := false
+ func() {
+ // should panic, then call mustRecover 7, which stops the panic.
+ // then should keep processing ordinary defers earlier than that one
+ // before returning.
+ // this test checks that the defer func on the next line actually runs.
+ defer func() { ok = true }()
+ defer mustRecover(7)
+ panic(7)
+ }()
+ if !ok {
+ println("did not run ok func")
+ die()
+ }
+}
+
+func varargs(s *int, a ...int) {
+ *s = 0
+ for _, v := range a {
+ *s += v
+ }
+ if recover() != nil {
+ *s += 100
+ }
+}
+
+func test8a() (r int) {
+ defer varargs(&r, 1, 2, 3)
+ panic(0)
+}
+
+func test8b() (r int) {
+ defer varargs(&r, 4, 5, 6)
+ return
+}
+
+func test8() {
+ if test8a() != 106 || test8b() != 15 {
+ println("wrong value")
+ die()
+ }
+}
+
+type I interface {
+ M()
+}
+
+// pointer receiver, so no wrapper in i.M()
+type T1 struct{}
+
+func (*T1) M() {
+ mustRecoverBody(doubleRecover(), recover(), recover(), 9)
+}
+
+func test9() {
+ var i I = &T1{}
+ defer i.M()
+ panic(9)
+}
+
+func test9reflect1() {
+ f := reflect.ValueOf(&T1{}).Method(0).Interface().(func())
+ defer f()
+ panic(9)
+}
+
+func test9reflect2() {
+ f := reflect.TypeOf(&T1{}).Method(0).Func.Interface().(func(*T1))
+ defer f(&T1{})
+ panic(9)
+}
+
+// word-sized value receiver, so no wrapper in i.M()
+type T2 uintptr
+
+func (T2) M() {
+ mustRecoverBody(doubleRecover(), recover(), recover(), 10)
+}
+
+func test10() {
+ var i I = T2(0)
+ defer i.M()
+ panic(10)
+}
+
+func test10reflect1() {
+ f := reflect.ValueOf(T2(0)).Method(0).Interface().(func())
+ defer f()
+ panic(10)
+}
+
+func test10reflect2() {
+ f := reflect.TypeOf(T2(0)).Method(0).Func.Interface().(func(T2))
+ defer f(T2(0))
+ panic(10)
+}
+
+// tiny receiver, so basic wrapper in i.M()
+type T3 struct{}
+
+func (T3) M() {
+ mustRecoverBody(doubleRecover(), recover(), recover(), 11)
+}
+
+func test11() {
+ var i I = T3{}
+ defer i.M()
+ panic(11)
+}
+
+func test11reflect1() {
+ f := reflect.ValueOf(T3{}).Method(0).Interface().(func())
+ defer f()
+ panic(11)
+}
+
+func test11reflect2() {
+ f := reflect.TypeOf(T3{}).Method(0).Func.Interface().(func(T3))
+ defer f(T3{})
+ panic(11)
+}
+
+// tiny receiver, so basic wrapper in i.M()
+type T3deeper struct{}
+
+func (T3deeper) M() {
+ badstate() // difference from T3
+ mustRecoverBody(doubleRecover(), recover(), recover(), 111)
+}
+
+func test111() {
+ var i I = T3deeper{}
+ defer i.M()
+ panic(111)
+}
+
+type Tiny struct{}
+
+func (Tiny) M() {
+ panic(112)
+}
+
+// i.M is a wrapper, and i.M panics.
+//
+// This is a torture test for an old implementation of recover that
+// tried to deal with wrapper functions by doing some argument
+// positioning math on both entry and exit. Doing anything on exit
+// is a problem because sometimes functions exit via panic instead
+// of an ordinary return, so panic would have to know to do the
+// same math when unwinding the stack. It gets complicated fast.
+// This particular test never worked with the old scheme, because
+// panic never did the right unwinding math.
+//
+// The new scheme adjusts Panic.argp on entry to a wrapper.
+// It has no exit work, so if a wrapper is interrupted by a panic,
+// there's no cleanup that panic itself must do.
+// This test just works now.
+func badstate() {
+ defer func() {
+ recover()
+ }()
+ var i I = Tiny{}
+ i.M()
+}
+
+// large receiver, so basic wrapper in i.M()
+type T4 [2]string
+
+func (T4) M() {
+ mustRecoverBody(doubleRecover(), recover(), recover(), 12)
+}
+
+func test12() {
+ var i I = T4{}
+ defer i.M()
+ panic(12)
+}
+
+func test12reflect1() {
+ f := reflect.ValueOf(T4{}).Method(0).Interface().(func())
+ defer f()
+ panic(12)
+}
+
+func test12reflect2() {
+ f := reflect.TypeOf(T4{}).Method(0).Func.Interface().(func(T4))
+ defer f(T4{})
+ panic(12)
+}
+
+// enormous receiver, so wrapper splits stack to call M
+type T5 [8192]byte
+
+func (T5) M() {
+ mustRecoverBody(doubleRecover(), recover(), recover(), 13)
+}
+
+func test13() {
+ var i I = T5{}
+ defer i.M()
+ panic(13)
+}
+
+func test13reflect1() {
+ f := reflect.ValueOf(T5{}).Method(0).Interface().(func())
+ defer f()
+ panic(13)
+}
+
+func test13reflect2() {
+ f := reflect.TypeOf(T5{}).Method(0).Func.Interface().(func(T5))
+ defer f(T5{})
+ panic(13)
+}
+
+// enormous receiver + enormous method frame, so wrapper splits stack to call M,
+// and then M splits stack to allocate its frame.
+// recover must look back two frames to find the panic.
+type T6 [8192]byte
+
+var global byte
+
+func (T6) M() {
+ var x [8192]byte
+ x[0] = 1
+ x[1] = 2
+ for i := range x {
+ global += x[i]
+ }
+ mustRecoverBody(doubleRecover(), recover(), recover(), 14)
+}
+
+func test14() {
+ var i I = T6{}
+ defer i.M()
+ panic(14)
+}
+
+func test14reflect1() {
+ f := reflect.ValueOf(T6{}).Method(0).Interface().(func())
+ defer f()
+ panic(14)
+}
+
+func test14reflect2() {
+ f := reflect.TypeOf(T6{}).Method(0).Func.Interface().(func(T6))
+ defer f(T6{})
+ panic(14)
+}
+
+// function created by reflect.MakeFunc
+
+func reflectFunc(args []reflect.Value) (results []reflect.Value) {
+ mustRecoverBody(doubleRecover(), recover(), recover(), 15)
+ return nil
+}
+
+func test15() {
+ f := reflect.MakeFunc(reflect.TypeOf((func())(nil)), reflectFunc).Interface().(func())
+ defer f()
+ panic(15)
+}
+
+func reflectFunc2(args []reflect.Value) (results []reflect.Value) {
+ // This will call reflectFunc3
+ args[0].Interface().(func())()
+ return nil
+}
+
+func reflectFunc3(args []reflect.Value) (results []reflect.Value) {
+ if v := recover(); v != nil {
+ println("spurious recover", v)
+ die()
+ }
+ return nil
+}
+
+func test16() {
+ defer mustRecover(16)
+
+ f2 := reflect.MakeFunc(reflect.TypeOf((func(func()))(nil)), reflectFunc2).Interface().(func(func()))
+ f3 := reflect.MakeFunc(reflect.TypeOf((func())(nil)), reflectFunc3).Interface().(func())
+ defer f2(f3)
+
+ panic(16)
+}