...
Run Format

Source file test/recover.go

Documentation: test

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

View as plain text