| // 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" |
| "runtime" |
| ) |
| |
| func main() { |
| test1() |
| test1WithClosures() |
| test2() |
| test3() |
| // exp/ssa/interp still has some bugs in recover(). |
| if os.Getenv("GOSSAINTERP") == "" { |
| 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() |
| } |
| } |
| |
| 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() |
| } |
| } |