]> Cypherpunks.ru repositories - gostls13.git/blobdiff - test/recover.go
cmd/compile/internal/inline: score call sites exposed by inlines
[gostls13.git] / test / recover.go
index d32cfdf3d1420fc389c298a2f844455336a37936..e4187c018f68a7e0d6b4bc7feb17af21e2ae4c4a 100644 (file)
@@ -1,6 +1,6 @@
 // run
 
-// Copyright 2010 The Go Authors.  All rights reserved.
+// 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.
 
@@ -8,29 +8,78 @@
 
 package main
 
-import "runtime"
+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()
-       test4()
+       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 mustRecover(x interface{}) {
-       mustNotRecover() // because it's not a defer call
-       v := recover()
+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")
+               println("missing recover", x.(int))
                die() // panic is useless here
        }
        if v != x {
@@ -39,13 +88,21 @@ func mustRecover(x interface{}) {
        }
 
        // the value should be gone now regardless
-       v = recover()
+       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 {
@@ -58,10 +115,23 @@ 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 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)
 }
 
@@ -82,7 +152,7 @@ func test1WithClosures() {
                mustNotRecover()
                v := recover()
                if v == nil {
-                       println("missing recover")
+                       println("missing recover", x.(int))
                        die()
                }
                if v != x {
@@ -244,3 +314,274 @@ func test7() {
                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)
+}