1// run
2
3// Copyright 2010 The Go Authors.  All rights reserved.
4// Use of this source code is governed by a BSD-style
5// license that can be found in the LICENSE file.
6
7// Test of basic recover functionality.
8
9package main
10
11import "runtime"
12
13func main() {
14	test1()
15	test1WithClosures()
16	test2()
17	test3()
18	test4()
19	test5()
20	test6()
21	test6WithClosures()
22	test7()
23}
24
25func die() {
26	runtime.Breakpoint() // can't depend on panic
27}
28
29func mustRecover(x interface{}) {
30	mustNotRecover() // because it's not a defer call
31	v := recover()
32	if v == nil {
33		println("missing recover")
34		die() // panic is useless here
35	}
36	if v != x {
37		println("wrong value", v, x)
38		die()
39	}
40
41	// the value should be gone now regardless
42	v = recover()
43	if v != nil {
44		println("recover didn't recover")
45		die()
46	}
47}
48
49func mustNotRecover() {
50	v := recover()
51	if v != nil {
52		println("spurious recover", v)
53		die()
54	}
55}
56
57func withoutRecover() {
58	mustNotRecover() // because it's a sub-call
59}
60
61func test1() {
62	defer mustNotRecover() // because mustRecover will squelch it
63	defer mustRecover(1)   // because of panic below
64	defer withoutRecover() // should be no-op, leaving for mustRecover to find
65	panic(1)
66}
67
68// Repeat test1 with closures instead of standard function.
69// Interesting because recover bases its decision
70// on the frame pointer of its caller, and a closure's
71// frame pointer is in the middle of its actual arguments
72// (after the hidden ones for the closed-over variables).
73func test1WithClosures() {
74	defer func() {
75		v := recover()
76		if v != nil {
77			println("spurious recover in closure")
78			die()
79		}
80	}()
81	defer func(x interface{}) {
82		mustNotRecover()
83		v := recover()
84		if v == nil {
85			println("missing recover")
86			die()
87		}
88		if v != x {
89			println("wrong value", v, x)
90			die()
91		}
92	}(1)
93	defer func() {
94		mustNotRecover()
95	}()
96	panic(1)
97}
98
99func test2() {
100	// Recover only sees the panic argument
101	// if it is called from a deferred call.
102	// It does not see the panic when called from a call within a deferred call (too late)
103	// nor does it see the panic when it *is* the deferred call (too early).
104	defer mustRecover(2)
105	defer recover() // should be no-op
106	panic(2)
107}
108
109func test3() {
110	defer mustNotRecover()
111	defer func() {
112		recover() // should squelch
113	}()
114	panic(3)
115}
116
117func test4() {
118	// Equivalent to test3 but using defer to make the call.
119	defer mustNotRecover()
120	defer func() {
121		defer recover() // should squelch
122	}()
123	panic(4)
124}
125
126// Check that closures can set output arguments.
127// Run g().  If it panics, return x; else return deflt.
128func try(g func(), deflt interface{}) (x interface{}) {
129	defer func() {
130		if v := recover(); v != nil {
131			x = v
132		}
133	}()
134	defer g()
135	return deflt
136}
137
138// Check that closures can set output arguments.
139// Run g().  If it panics, return x; else return deflt.
140func try1(g func(), deflt interface{}) (x interface{}) {
141	defer func() {
142		if v := recover(); v != nil {
143			x = v
144		}
145	}()
146	defer g()
147	x = deflt
148	return
149}
150
151func test5() {
152	v := try(func() { panic(5) }, 55).(int)
153	if v != 5 {
154		println("wrong value", v, 5)
155		die()
156	}
157
158	s := try(func() {}, "hi").(string)
159	if s != "hi" {
160		println("wrong value", s, "hi")
161		die()
162	}
163
164	v = try1(func() { panic(5) }, 55).(int)
165	if v != 5 {
166		println("try1 wrong value", v, 5)
167		die()
168	}
169
170	s = try1(func() {}, "hi").(string)
171	if s != "hi" {
172		println("try1 wrong value", s, "hi")
173		die()
174	}
175}
176
177// When a deferred big call starts, it must first
178// create yet another stack segment to hold the
179// giant frame for x.  Make sure that doesn't
180// confuse recover.
181func big(mustRecover bool) {
182	var x [100000]int
183	x[0] = 1
184	x[99999] = 1
185	_ = x
186
187	v := recover()
188	if mustRecover {
189		if v == nil {
190			println("missing big recover")
191			die()
192		}
193	} else {
194		if v != nil {
195			println("spurious big recover")
196			die()
197		}
198	}
199}
200
201func test6() {
202	defer big(false)
203	defer big(true)
204	panic(6)
205}
206
207func test6WithClosures() {
208	defer func() {
209		var x [100000]int
210		x[0] = 1
211		x[99999] = 1
212		_ = x
213		if recover() != nil {
214			println("spurious big closure recover")
215			die()
216		}
217	}()
218	defer func() {
219		var x [100000]int
220		x[0] = 1
221		x[99999] = 1
222		_ = x
223		if recover() == nil {
224			println("missing big closure recover")
225			die()
226		}
227	}()
228	panic("6WithClosures")
229}
230
231func test7() {
232	ok := false
233	func() {
234		// should panic, then call mustRecover 7, which stops the panic.
235		// then should keep processing ordinary defers earlier than that one
236		// before returning.
237		// this test checks that the defer func on the next line actually runs.
238		defer func() { ok = true }()
239		defer mustRecover(7)
240		panic(7)
241	}()
242	if !ok {
243		println("did not run ok func")
244		die()
245	}
246}
247
248func varargs(s *int, a ...int) {
249	*s = 0
250	for _, v := range a {
251		*s += v
252	}
253	if recover() != nil {
254		*s += 100
255	}
256}
257
258func test8a() (r int) {
259	defer varargs(&r, 1, 2, 3)
260	panic(0)
261}
262
263func test8b() (r int) {
264	defer varargs(&r, 4, 5, 6)
265	return
266}
267
268func test8() {
269	if test8a() != 106 || test8b() != 15 {
270		println("wrong value")
271		die()
272	}
273}
274