diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 13:14:23 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 13:14:23 +0000 |
commit | 73df946d56c74384511a194dd01dbe099584fd1a (patch) | |
tree | fd0bcea490dd81327ddfbb31e215439672c9a068 /test/recover.go | |
parent | Initial commit. (diff) | |
download | golang-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.go | 587 |
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) +} |