1// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package runtime_test
6
7import (
8	"fmt"
9	"os"
10	"reflect"
11	"runtime"
12	"testing"
13)
14
15// Make sure open-coded defer exit code is not lost, even when there is an
16// unconditional panic (hence no return from the function)
17func TestUnconditionalPanic(t *testing.T) {
18	defer func() {
19		if recover() != "testUnconditional" {
20			t.Fatal("expected unconditional panic")
21		}
22	}()
23	panic("testUnconditional")
24}
25
26var glob int = 3
27
28// Test an open-coded defer and non-open-coded defer - make sure both defers run
29// and call recover()
30func TestOpenAndNonOpenDefers(t *testing.T) {
31	for {
32		// Non-open defer because in a loop
33		defer func(n int) {
34			if recover() != "testNonOpenDefer" {
35				t.Fatal("expected testNonOpen panic")
36			}
37		}(3)
38		if glob > 2 {
39			break
40		}
41	}
42	testOpen(t, 47)
43	panic("testNonOpenDefer")
44}
45
46//go:noinline
47func testOpen(t *testing.T, arg int) {
48	defer func(n int) {
49		if recover() != "testOpenDefer" {
50			t.Fatal("expected testOpen panic")
51		}
52	}(4)
53	if arg > 2 {
54		panic("testOpenDefer")
55	}
56}
57
58// Test a non-open-coded defer and an open-coded defer - make sure both defers run
59// and call recover()
60func TestNonOpenAndOpenDefers(t *testing.T) {
61	testOpen(t, 47)
62	for {
63		// Non-open defer because in a loop
64		defer func(n int) {
65			if recover() != "testNonOpenDefer" {
66				t.Fatal("expected testNonOpen panic")
67			}
68		}(3)
69		if glob > 2 {
70			break
71		}
72	}
73	panic("testNonOpenDefer")
74}
75
76var list []int
77
78// Make sure that conditional open-coded defers are activated correctly and run in
79// the correct order.
80func TestConditionalDefers(t *testing.T) {
81	list = make([]int, 0, 10)
82
83	defer func() {
84		if recover() != "testConditional" {
85			t.Fatal("expected panic")
86		}
87		want := []int{4, 2, 1}
88		if !reflect.DeepEqual(want, list) {
89			t.Fatal(fmt.Sprintf("wanted %v, got %v", want, list))
90		}
91
92	}()
93	testConditionalDefers(8)
94}
95
96func testConditionalDefers(n int) {
97	doappend := func(i int) {
98		list = append(list, i)
99	}
100
101	defer doappend(1)
102	if n > 5 {
103		defer doappend(2)
104		if n > 8 {
105			defer doappend(3)
106		} else {
107			defer doappend(4)
108		}
109	}
110	panic("testConditional")
111}
112
113// Test that there is no compile-time or run-time error if an open-coded defer
114// call is removed by constant propagation and dead-code elimination.
115func TestDisappearingDefer(t *testing.T) {
116	switch runtime.GOOS {
117	case "invalidOS":
118		defer func() {
119			t.Fatal("Defer shouldn't run")
120		}()
121	}
122}
123
124// This tests an extra recursive panic behavior that is only specified in the
125// code. Suppose a first panic P1 happens and starts processing defer calls. If a
126// second panic P2 happens while processing defer call D in frame F, then defer
127// call processing is restarted (with some potentially new defer calls created by
128// D or its callees). If the defer processing reaches the started defer call D
129// again in the defer stack, then the original panic P1 is aborted and cannot
130// continue panic processing or be recovered. If the panic P2 does a recover at
131// some point, it will naturally remove the original panic P1 from the stack
132// (since the original panic had to be in frame F or a descendant of F).
133func TestAbortedPanic(t *testing.T) {
134	defer func() {
135		r := recover()
136		if r != nil {
137			t.Fatal(fmt.Sprintf("wanted nil recover, got %v", r))
138		}
139	}()
140	defer func() {
141		r := recover()
142		if r != "panic2" {
143			t.Fatal(fmt.Sprintf("wanted %v, got %v", "panic2", r))
144		}
145	}()
146	defer func() {
147		panic("panic2")
148	}()
149	panic("panic1")
150}
151
152// This tests that recover() does not succeed unless it is called directly from a
153// defer function that is directly called by the panic.  Here, we first call it
154// from a defer function that is created by the defer function called directly by
155// the panic.  In
156func TestRecoverMatching(t *testing.T) {
157	defer func() {
158		r := recover()
159		if r != "panic1" {
160			t.Fatal(fmt.Sprintf("wanted %v, got %v", "panic1", r))
161		}
162	}()
163	defer func() {
164		defer func() {
165			// Shouldn't succeed, even though it is called directly
166			// from a defer function, since this defer function was
167			// not directly called by the panic.
168			r := recover()
169			if r != nil {
170				t.Fatal(fmt.Sprintf("wanted nil recover, got %v", r))
171			}
172		}()
173	}()
174	panic("panic1")
175}
176
177type nonSSAable [128]byte
178
179type bigStruct struct {
180	x, y, z, w, p, q int64
181}
182
183type containsBigStruct struct {
184	element bigStruct
185}
186
187func mknonSSAable() nonSSAable {
188	globint1++
189	return nonSSAable{0, 0, 0, 0, 5}
190}
191
192var globint1, globint2, globint3 int
193
194//go:noinline
195func sideeffect(n int64) int64 {
196	globint2++
197	return n
198}
199
200func sideeffect2(in containsBigStruct) containsBigStruct {
201	globint3++
202	return in
203}
204
205// Test that nonSSAable arguments to defer are handled correctly and only evaluated once.
206func TestNonSSAableArgs(t *testing.T) {
207	globint1 = 0
208	globint2 = 0
209	globint3 = 0
210	var save1 byte
211	var save2 int64
212	var save3 int64
213	var save4 int64
214
215	defer func() {
216		if globint1 != 1 {
217			t.Fatal(fmt.Sprintf("globint1:  wanted: 1, got %v", globint1))
218		}
219		if save1 != 5 {
220			t.Fatal(fmt.Sprintf("save1:  wanted: 5, got %v", save1))
221		}
222		if globint2 != 1 {
223			t.Fatal(fmt.Sprintf("globint2:  wanted: 1, got %v", globint2))
224		}
225		if save2 != 2 {
226			t.Fatal(fmt.Sprintf("save2:  wanted: 2, got %v", save2))
227		}
228		if save3 != 4 {
229			t.Fatal(fmt.Sprintf("save3:  wanted: 4, got %v", save3))
230		}
231		if globint3 != 1 {
232			t.Fatal(fmt.Sprintf("globint3:  wanted: 1, got %v", globint3))
233		}
234		if save4 != 4 {
235			t.Fatal(fmt.Sprintf("save1:  wanted: 4, got %v", save4))
236		}
237	}()
238
239	// Test function returning a non-SSAable arg
240	defer func(n nonSSAable) {
241		save1 = n[4]
242	}(mknonSSAable())
243	// Test composite literal that is not SSAable
244	defer func(b bigStruct) {
245		save2 = b.y
246	}(bigStruct{1, 2, 3, 4, 5, sideeffect(6)})
247
248	// Test struct field reference that is non-SSAable
249	foo := containsBigStruct{}
250	foo.element.z = 4
251	defer func(element bigStruct) {
252		save3 = element.z
253	}(foo.element)
254	defer func(element bigStruct) {
255		save4 = element.z
256	}(sideeffect2(foo).element)
257}
258
259//go:noinline
260func doPanic() {
261	panic("Test panic")
262}
263
264func TestDeferForFuncWithNoExit(t *testing.T) {
265	cond := 1
266	defer func() {
267		if cond != 2 {
268			t.Fatal(fmt.Sprintf("cond: wanted 2, got %v", cond))
269		}
270		if recover() != "Test panic" {
271			t.Fatal("Didn't find expected panic")
272		}
273	}()
274	x := 0
275	// Force a stack copy, to make sure that the &cond pointer passed to defer
276	// function is properly updated.
277	growStackIter(&x, 1000)
278	cond = 2
279	doPanic()
280
281	// This function has no exit/return, since it ends with an infinite loop
282	for {
283	}
284}
285
286// Test case approximating issue #37664, where a recursive function (interpreter)
287// may do repeated recovers/re-panics until it reaches the frame where the panic
288// can actually be handled. The recurseFnPanicRec() function is testing that there
289// are no stale defer structs on the defer chain after the interpreter() sequence,
290// by writing a bunch of 0xffffffffs into several recursive stack frames, and then
291// doing a single panic-recover which would invoke any such stale defer structs.
292func TestDeferWithRepeatedRepanics(t *testing.T) {
293	interpreter(0, 6, 2)
294	recurseFnPanicRec(0, 10)
295	interpreter(0, 5, 1)
296	recurseFnPanicRec(0, 10)
297	interpreter(0, 6, 3)
298	recurseFnPanicRec(0, 10)
299}
300
301func interpreter(level int, maxlevel int, rec int) {
302	defer func() {
303		e := recover()
304		if e == nil {
305			return
306		}
307		if level != e.(int) {
308			//fmt.Fprintln(os.Stderr, "re-panicing, level", level)
309			panic(e)
310		}
311		//fmt.Fprintln(os.Stderr, "Recovered, level", level)
312	}()
313	if level+1 < maxlevel {
314		interpreter(level+1, maxlevel, rec)
315	} else {
316		//fmt.Fprintln(os.Stderr, "Initiating panic")
317		panic(rec)
318	}
319}
320
321func recurseFnPanicRec(level int, maxlevel int) {
322	defer func() {
323		recover()
324	}()
325	recurseFn(level, maxlevel)
326}
327
328func recurseFn(level int, maxlevel int) {
329	a := [40]uint32{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}
330	if level+1 < maxlevel {
331		// Need this print statement to keep a around.  '_ = a[4]' doesn't do it.
332		fmt.Fprintln(os.Stderr, "recurseFn", level, a[4])
333		recurseFn(level+1, maxlevel)
334	} else {
335		panic("recurseFn panic")
336	}
337}
338
339// Try to reproduce issue #37688, where a pointer to an open-coded defer struct is
340// mistakenly held, and that struct keeps a pointer to a stack-allocated defer
341// struct, and that stack-allocated struct gets overwritten or the stack gets
342// moved, so a memory error happens on GC.
343func TestIssue37688(t *testing.T) {
344	for j := 0; j < 10; j++ {
345		g2()
346		g3()
347	}
348}
349
350type foo struct {
351}
352
353func (f *foo) method1() {
354	fmt.Fprintln(os.Stderr, "method1")
355}
356
357func (f *foo) method2() {
358	fmt.Fprintln(os.Stderr, "method2")
359}
360
361func g2() {
362	var a foo
363	ap := &a
364	// The loop forces this defer to be heap-allocated and the remaining two
365	// to be stack-allocated.
366	for i := 0; i < 1; i++ {
367		defer ap.method1()
368	}
369	defer ap.method2()
370	defer ap.method1()
371	ff1(ap, 1, 2, 3, 4, 5, 6, 7, 8, 9)
372	// Try to get the stack to be be moved by growing it too large, so
373	// existing stack-allocated defer becomes invalid.
374	rec1(2000)
375}
376
377func g3() {
378	// Mix up the stack layout by adding in an extra function frame
379	g2()
380}
381
382func ff1(ap *foo, a, b, c, d, e, f, g, h, i int) {
383	defer ap.method1()
384
385	// Make a defer that has a very large set of args, hence big size for the
386	// defer record for the open-coded frame (which means it won't use the
387	// defer pool)
388	defer func(ap *foo, a, b, c, d, e, f, g, h, i int) {
389		if v := recover(); v != nil {
390			fmt.Fprintln(os.Stderr, "did recover")
391		}
392		fmt.Fprintln(os.Stderr, "debug", ap, a, b, c, d, e, f, g, h)
393	}(ap, a, b, c, d, e, f, g, h, i)
394	panic("ff1 panic")
395}
396
397func rec1(max int) {
398	if max > 0 {
399		rec1(max - 1)
400	} else {
401		fmt.Fprintln(os.Stderr, "finished recursion", max)
402	}
403}
404