golang recover 代码
文件路径:/test/recover.go
/ 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)
}