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