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