|  | // $G $D/$F.go && $L $F.$A && ./$A.out | 
|  |  | 
|  | // 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 "runtime" | 
|  |  | 
|  | func main() { | 
|  | test1() | 
|  | test1WithClosures() | 
|  | test2() | 
|  | test3() | 
|  | test4() | 
|  | test5() | 
|  | test6() | 
|  | test6WithClosures() | 
|  | test7() | 
|  | } | 
|  |  | 
|  | func die() { | 
|  | runtime.Breakpoint() // can't depend on panic | 
|  | } | 
|  |  | 
|  | func mustRecover(x interface{}) { | 
|  | mustNotRecover() // because it's not a defer call | 
|  | v := recover() | 
|  | if v == nil { | 
|  | println("missing recover") | 
|  | die() // panic is useless here | 
|  | } | 
|  | if v != x { | 
|  | println("wrong value", v, x) | 
|  | die() | 
|  | } | 
|  |  | 
|  | // the value should be gone now regardless | 
|  | v = recover() | 
|  | if v != nil { | 
|  | println("recover didn't recover") | 
|  | die() | 
|  | } | 
|  | } | 
|  |  | 
|  | func mustNotRecover() { | 
|  | v := recover() | 
|  | if v != nil { | 
|  | println("spurious recover", v) | 
|  | die() | 
|  | } | 
|  | } | 
|  |  | 
|  | func withoutRecover() { | 
|  | mustNotRecover() // because it's a sub-call | 
|  | } | 
|  |  | 
|  | 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 | 
|  | 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") | 
|  | 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() | 
|  | } | 
|  | } |