| // 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) | |
| } |