github.com/x04/go/src@v0.0.0-20200202162449-3d481ceb3525/runtime/defer_test.go (about)

     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  	"github.com/x04/go/src/fmt"
     9  	"github.com/x04/go/src/reflect"
    10  	"github.com/x04/go/src/runtime"
    11  	"github.com/x04/go/src/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)
    16  func TestUnconditionalPanic(t *testing.T) {
    17  	defer func() {
    18  		if recover() != "testUnconditional" {
    19  			t.Fatal("expected unconditional panic")
    20  		}
    21  	}()
    22  	panic("testUnconditional")
    23  }
    24  
    25  var glob int = 3
    26  
    27  // Test an open-coded defer and non-open-coded defer - make sure both defers run
    28  // and call recover()
    29  func 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
    46  func 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()
    59  func 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  
    75  var list []int
    76  
    77  // Make sure that conditional open-coded defers are activated correctly and run in
    78  // the correct order.
    79  func 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  
    95  func 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.
   114  func 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).
   132  func 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
   155  func 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  
   176  type nonSSAable [128]byte
   177  
   178  type bigStruct struct {
   179  	x, y, z, w, p, q int64
   180  }
   181  
   182  type containsBigStruct struct {
   183  	element bigStruct
   184  }
   185  
   186  func mknonSSAable() nonSSAable {
   187  	globint1++
   188  	return nonSSAable{0, 0, 0, 0, 5}
   189  }
   190  
   191  var globint1, globint2, globint3 int
   192  
   193  //go:noinline
   194  func sideeffect(n int64) int64 {
   195  	globint2++
   196  	return n
   197  }
   198  
   199  func 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.
   205  func 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
   259  func doPanic() {
   260  	panic("Test panic")
   261  }
   262  
   263  func 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  }