| // Copyright 2019 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. | 
 |  | 
 | package runtime_test | 
 |  | 
 | import ( | 
 | 	"fmt" | 
 | 	"reflect" | 
 | 	"runtime" | 
 | 	"testing" | 
 | ) | 
 |  | 
 | // Make sure open-coded defer exit code is not lost, even when there is an | 
 | // unconditional panic (hence no return from the function) | 
 | func TestUnconditionalPanic(t *testing.T) { | 
 | 	defer func() { | 
 | 		if recover() != "testUnconditional" { | 
 | 			t.Fatal("expected unconditional panic") | 
 | 		} | 
 | 	}() | 
 | 	panic("testUnconditional") | 
 | } | 
 |  | 
 | var glob int = 3 | 
 |  | 
 | // Test an open-coded defer and non-open-coded defer - make sure both defers run | 
 | // and call recover() | 
 | func TestOpenAndNonOpenDefers(t *testing.T) { | 
 | 	for { | 
 | 		// Non-open defer because in a loop | 
 | 		defer func(n int) { | 
 | 			if recover() != "testNonOpenDefer" { | 
 | 				t.Fatal("expected testNonOpen panic") | 
 | 			} | 
 | 		}(3) | 
 | 		if glob > 2 { | 
 | 			break | 
 | 		} | 
 | 	} | 
 | 	testOpen(t, 47) | 
 | 	panic("testNonOpenDefer") | 
 | } | 
 |  | 
 | //go:noinline | 
 | func testOpen(t *testing.T, arg int) { | 
 | 	defer func(n int) { | 
 | 		if recover() != "testOpenDefer" { | 
 | 			t.Fatal("expected testOpen panic") | 
 | 		} | 
 | 	}(4) | 
 | 	if arg > 2 { | 
 | 		panic("testOpenDefer") | 
 | 	} | 
 | } | 
 |  | 
 | // Test a non-open-coded defer and an open-coded defer - make sure both defers run | 
 | // and call recover() | 
 | func TestNonOpenAndOpenDefers(t *testing.T) { | 
 | 	testOpen(t, 47) | 
 | 	for { | 
 | 		// Non-open defer because in a loop | 
 | 		defer func(n int) { | 
 | 			if recover() != "testNonOpenDefer" { | 
 | 				t.Fatal("expected testNonOpen panic") | 
 | 			} | 
 | 		}(3) | 
 | 		if glob > 2 { | 
 | 			break | 
 | 		} | 
 | 	} | 
 | 	panic("testNonOpenDefer") | 
 | } | 
 |  | 
 | var list []int | 
 |  | 
 | // Make sure that conditional open-coded defers are activated correctly and run in | 
 | // the correct order. | 
 | func TestConditionalDefers(t *testing.T) { | 
 | 	list = make([]int, 0, 10) | 
 |  | 
 | 	defer func() { | 
 | 		if recover() != "testConditional" { | 
 | 			t.Fatal("expected panic") | 
 | 		} | 
 | 		want := []int{4, 2, 1} | 
 | 		if !reflect.DeepEqual(want, list) { | 
 | 			t.Fatal(fmt.Sprintf("wanted %v, got %v", want, list)) | 
 | 		} | 
 |  | 
 | 	}() | 
 | 	testConditionalDefers(8) | 
 | } | 
 |  | 
 | func testConditionalDefers(n int) { | 
 | 	doappend := func(i int) { | 
 | 		list = append(list, i) | 
 | 	} | 
 |  | 
 | 	defer doappend(1) | 
 | 	if n > 5 { | 
 | 		defer doappend(2) | 
 | 		if n > 8 { | 
 | 			defer doappend(3) | 
 | 		} else { | 
 | 			defer doappend(4) | 
 | 		} | 
 | 	} | 
 | 	panic("testConditional") | 
 | } | 
 |  | 
 | // Test that there is no compile-time or run-time error if an open-coded defer | 
 | // call is removed by constant propagation and dead-code elimination. | 
 | func TestDisappearingDefer(t *testing.T) { | 
 | 	switch runtime.GOOS { | 
 | 	case "invalidOS": | 
 | 		defer func() { | 
 | 			t.Fatal("Defer shouldn't run") | 
 | 		}() | 
 | 	} | 
 | } | 
 |  | 
 | // This tests an extra recursive panic behavior that is only specified in the | 
 | // code. Suppose a first panic P1 happens and starts processing defer calls. If a | 
 | // second panic P2 happens while processing defer call D in frame F, then defer | 
 | // call processing is restarted (with some potentially new defer calls created by | 
 | // D or its callees). If the defer processing reaches the started defer call D | 
 | // again in the defer stack, then the original panic P1 is aborted and cannot | 
 | // continue panic processing or be recovered. If the panic P2 does a recover at | 
 | // some point, it will naturally remove the original panic P1 from the stack | 
 | // (since the original panic had to be in frame F or a descendant of F). | 
 | func TestAbortedPanic(t *testing.T) { | 
 | 	defer func() { | 
 | 		r := recover() | 
 | 		if r != nil { | 
 | 			t.Fatal(fmt.Sprintf("wanted nil recover, got %v", r)) | 
 | 		} | 
 | 	}() | 
 | 	defer func() { | 
 | 		r := recover() | 
 | 		if r != "panic2" { | 
 | 			t.Fatal(fmt.Sprintf("wanted %v, got %v", "panic2", r)) | 
 | 		} | 
 | 	}() | 
 | 	defer func() { | 
 | 		panic("panic2") | 
 | 	}() | 
 | 	panic("panic1") | 
 | } | 
 |  | 
 | // This tests that recover() does not succeed unless it is called directly from a | 
 | // defer function that is directly called by the panic.  Here, we first call it | 
 | // from a defer function that is created by the defer function called directly by | 
 | // the panic.  In | 
 | func TestRecoverMatching(t *testing.T) { | 
 | 	defer func() { | 
 | 		r := recover() | 
 | 		if r != "panic1" { | 
 | 			t.Fatal(fmt.Sprintf("wanted %v, got %v", "panic1", r)) | 
 | 		} | 
 | 	}() | 
 | 	defer func() { | 
 | 		defer func() { | 
 | 			// Shouldn't succeed, even though it is called directly | 
 | 			// from a defer function, since this defer function was | 
 | 			// not directly called by the panic. | 
 | 			r := recover() | 
 | 			if r != nil { | 
 | 				t.Fatal(fmt.Sprintf("wanted nil recover, got %v", r)) | 
 | 			} | 
 | 		}() | 
 | 	}() | 
 | 	panic("panic1") | 
 | } | 
 |  | 
 | type nonSSAable [128]byte | 
 |  | 
 | type bigStruct struct { | 
 | 	x, y, z, w, p, q int64 | 
 | } | 
 |  | 
 | type containsBigStruct struct { | 
 | 	element bigStruct | 
 | } | 
 |  | 
 | func mknonSSAable() nonSSAable { | 
 | 	globint1++ | 
 | 	return nonSSAable{0, 0, 0, 0, 5} | 
 | } | 
 |  | 
 | var globint1, globint2, globint3 int | 
 |  | 
 | //go:noinline | 
 | func sideeffect(n int64) int64 { | 
 | 	globint2++ | 
 | 	return n | 
 | } | 
 |  | 
 | func sideeffect2(in containsBigStruct) containsBigStruct { | 
 | 	globint3++ | 
 | 	return in | 
 | } | 
 |  | 
 | // Test that nonSSAable arguments to defer are handled correctly and only evaluated once. | 
 | func TestNonSSAableArgs(t *testing.T) { | 
 | 	globint1 = 0 | 
 | 	globint2 = 0 | 
 | 	globint3 = 0 | 
 | 	var save1 byte | 
 | 	var save2 int64 | 
 | 	var save3 int64 | 
 | 	var save4 int64 | 
 |  | 
 | 	defer func() { | 
 | 		if globint1 != 1 { | 
 | 			t.Fatal(fmt.Sprintf("globint1:  wanted: 1, got %v", globint1)) | 
 | 		} | 
 | 		if save1 != 5 { | 
 | 			t.Fatal(fmt.Sprintf("save1:  wanted: 5, got %v", save1)) | 
 | 		} | 
 | 		if globint2 != 1 { | 
 | 			t.Fatal(fmt.Sprintf("globint2:  wanted: 1, got %v", globint2)) | 
 | 		} | 
 | 		if save2 != 2 { | 
 | 			t.Fatal(fmt.Sprintf("save2:  wanted: 2, got %v", save2)) | 
 | 		} | 
 | 		if save3 != 4 { | 
 | 			t.Fatal(fmt.Sprintf("save3:  wanted: 4, got %v", save3)) | 
 | 		} | 
 | 		if globint3 != 1 { | 
 | 			t.Fatal(fmt.Sprintf("globint3:  wanted: 1, got %v", globint3)) | 
 | 		} | 
 | 		if save4 != 4 { | 
 | 			t.Fatal(fmt.Sprintf("save1:  wanted: 4, got %v", save4)) | 
 | 		} | 
 | 	}() | 
 |  | 
 | 	// Test function returning a non-SSAable arg | 
 | 	defer func(n nonSSAable) { | 
 | 		save1 = n[4] | 
 | 	}(mknonSSAable()) | 
 | 	// Test composite literal that is not SSAable | 
 | 	defer func(b bigStruct) { | 
 | 		save2 = b.y | 
 | 	}(bigStruct{1, 2, 3, 4, 5, sideeffect(6)}) | 
 |  | 
 | 	// Test struct field reference that is non-SSAable | 
 | 	foo := containsBigStruct{} | 
 | 	foo.element.z = 4 | 
 | 	defer func(element bigStruct) { | 
 | 		save3 = element.z | 
 | 	}(foo.element) | 
 | 	defer func(element bigStruct) { | 
 | 		save4 = element.z | 
 | 	}(sideeffect2(foo).element) | 
 | } | 
 |  | 
 | //go:noinline | 
 | func doPanic() { | 
 | 	panic("Test panic") | 
 | } | 
 |  | 
 | func TestDeferForFuncWithNoExit(t *testing.T) { | 
 | 	cond := 1 | 
 | 	defer func() { | 
 | 		if cond != 2 { | 
 | 			t.Fatal(fmt.Sprintf("cond: wanted 2, got %v", cond)) | 
 | 		} | 
 | 		if recover() != "Test panic" { | 
 | 			t.Fatal("Didn't find expected panic") | 
 | 		} | 
 | 	}() | 
 | 	x := 0 | 
 | 	// Force a stack copy, to make sure that the &cond pointer passed to defer | 
 | 	// function is properly updated. | 
 | 	growStackIter(&x, 1000) | 
 | 	cond = 2 | 
 | 	doPanic() | 
 |  | 
 | 	// This function has no exit/return, since it ends with an infinite loop | 
 | 	for { | 
 | 	} | 
 | } | 
 |  | 
 | // Test case approximating issue #37664, where a recursive function (interpreter) | 
 | // may do repeated recovers/re-panics until it reaches the frame where the panic | 
 | // can actually be handled. The recurseFnPanicRec() function is testing that there | 
 | // are no stale defer structs on the defer chain after the interpreter() sequence, | 
 | // by writing a bunch of 0xffffffffs into several recursive stack frames, and then | 
 | // doing a single panic-recover which would invoke any such stale defer structs. | 
 | func TestDeferWithRepeatedRepanics(t *testing.T) { | 
 | 	interpreter(0, 6, 2) | 
 | 	recurseFnPanicRec(0, 10) | 
 | 	interpreter(0, 5, 1) | 
 | 	recurseFnPanicRec(0, 10) | 
 | 	interpreter(0, 6, 3) | 
 | 	recurseFnPanicRec(0, 10) | 
 | } | 
 |  | 
 | func interpreter(level int, maxlevel int, rec int) { | 
 | 	defer func() { | 
 | 		e := recover() | 
 | 		if e == nil { | 
 | 			return | 
 | 		} | 
 | 		if level != e.(int) { | 
 | 			//fmt.Fprintln(os.Stderr, "re-panicing, level", level) | 
 | 			panic(e) | 
 | 		} | 
 | 		//fmt.Fprintln(os.Stderr, "Recovered, level", level) | 
 | 	}() | 
 | 	if level+1 < maxlevel { | 
 | 		interpreter(level+1, maxlevel, rec) | 
 | 	} else { | 
 | 		//fmt.Fprintln(os.Stderr, "Initiating panic") | 
 | 		panic(rec) | 
 | 	} | 
 | } | 
 |  | 
 | func recurseFnPanicRec(level int, maxlevel int) { | 
 | 	defer func() { | 
 | 		recover() | 
 | 	}() | 
 | 	recurseFn(level, maxlevel) | 
 | } | 
 |  | 
 | var saveInt uint32 | 
 |  | 
 | func recurseFn(level int, maxlevel int) { | 
 | 	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} | 
 | 	if level+1 < maxlevel { | 
 | 		// Make sure a array is referenced, so it is not optimized away | 
 | 		saveInt = a[4] | 
 | 		recurseFn(level+1, maxlevel) | 
 | 	} else { | 
 | 		panic("recurseFn panic") | 
 | 	} | 
 | } | 
 |  | 
 | // Try to reproduce issue #37688, where a pointer to an open-coded defer struct is | 
 | // mistakenly held, and that struct keeps a pointer to a stack-allocated defer | 
 | // struct, and that stack-allocated struct gets overwritten or the stack gets | 
 | // moved, so a memory error happens on GC. | 
 | func TestIssue37688(t *testing.T) { | 
 | 	for j := 0; j < 10; j++ { | 
 | 		g2() | 
 | 		g3() | 
 | 	} | 
 | } | 
 |  | 
 | type foo struct { | 
 | } | 
 |  | 
 | //go:noinline | 
 | func (f *foo) method1() { | 
 | } | 
 |  | 
 | //go:noinline | 
 | func (f *foo) method2() { | 
 | } | 
 |  | 
 | func g2() { | 
 | 	var a foo | 
 | 	ap := &a | 
 | 	// The loop forces this defer to be heap-allocated and the remaining two | 
 | 	// to be stack-allocated. | 
 | 	for i := 0; i < 1; i++ { | 
 | 		defer ap.method1() | 
 | 	} | 
 | 	defer ap.method2() | 
 | 	defer ap.method1() | 
 | 	ff1(ap, 1, 2, 3, 4, 5, 6, 7, 8, 9) | 
 | 	// Try to get the stack to be be moved by growing it too large, so | 
 | 	// existing stack-allocated defer becomes invalid. | 
 | 	rec1(2000) | 
 | } | 
 |  | 
 | func g3() { | 
 | 	// Mix up the stack layout by adding in an extra function frame | 
 | 	g2() | 
 | } | 
 |  | 
 | var globstruct struct { | 
 | 	a, b, c, d, e, f, g, h, i int | 
 | } | 
 |  | 
 | func ff1(ap *foo, a, b, c, d, e, f, g, h, i int) { | 
 | 	defer ap.method1() | 
 |  | 
 | 	// Make a defer that has a very large set of args, hence big size for the | 
 | 	// defer record for the open-coded frame (which means it won't use the | 
 | 	// defer pool) | 
 | 	defer func(ap *foo, a, b, c, d, e, f, g, h, i int) { | 
 | 		if v := recover(); v != nil { | 
 | 		} | 
 | 		globstruct.a = a | 
 | 		globstruct.b = b | 
 | 		globstruct.c = c | 
 | 		globstruct.d = d | 
 | 		globstruct.e = e | 
 | 		globstruct.f = f | 
 | 		globstruct.g = g | 
 | 		globstruct.h = h | 
 | 	}(ap, a, b, c, d, e, f, g, h, i) | 
 | 	panic("ff1 panic") | 
 | } | 
 |  | 
 | func rec1(max int) { | 
 | 	if max > 0 { | 
 | 		rec1(max - 1) | 
 | 	} | 
 | } |