Source file src/runtime/defer_test.go

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

View as plain text