github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/compiler/defer_test.go (about)

     1  package compiler_test
     2  
     3  import (
     4  	"math/big"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func TestDefer(t *testing.T) {
    13  	t.Run("Simple", func(t *testing.T) {
    14  		src := `package main
    15  		var a int
    16  		func Main() int {
    17  			return h() + a
    18  		}
    19  		func h() int {
    20  			defer f()
    21  			return 1
    22  		}
    23  		func f() { a += 2 }`
    24  		eval(t, src, big.NewInt(3))
    25  	})
    26  	t.Run("ValueUnchanged", func(t *testing.T) {
    27  		src := `package main
    28  		var a int
    29  		func Main() int {
    30  			defer f()
    31  			a = 3
    32  			return a
    33  		}
    34  		func f() { a += 2 }`
    35  		eval(t, src, big.NewInt(3))
    36  	})
    37  	t.Run("Function", func(t *testing.T) {
    38  		src := `package main
    39  		var a int
    40  		func Main() int {
    41  			return h() + a
    42  		}
    43  		func h() int {
    44  			defer f()
    45  			a = 3
    46  			return g()
    47  		}
    48  		func g() int {
    49  			a++
    50  			return a
    51  		}
    52  		func f() { a += 2 }`
    53  		eval(t, src, big.NewInt(10))
    54  	})
    55  	t.Run("DeferAfterInterop", func(t *testing.T) {
    56  		src := `package main
    57  
    58  		import (
    59  			"github.com/nspcc-dev/neo-go/pkg/interop/storage"
    60  		)
    61  
    62  		func Main() {
    63  			defer func() {
    64  			}()
    65  			storage.GetContext()
    66  		}`
    67  		vm := vmAndCompile(t, src)
    68  		err := vm.Run()
    69  		require.NoError(t, err)
    70  		require.Equal(t, 0, vm.Estack().Len(), "stack contains unexpected items")
    71  	})
    72  
    73  	t.Run("MultipleDefers", func(t *testing.T) {
    74  		src := `package main
    75  		var a int
    76  		func Main() int {
    77  			return h() + a
    78  		}
    79  		func h() int {
    80  			defer f()
    81  			defer g()
    82  			a = 3
    83  			return a
    84  		}
    85  		func g() { a *= 2 }
    86  		func f() { a += 2 }`
    87  		eval(t, src, big.NewInt(11))
    88  	})
    89  	t.Run("FunctionLiteral", func(t *testing.T) {
    90  		src := `package main
    91  		var a int
    92  		func Main() int {
    93  			return h() + a
    94  		}
    95  		func h() int {
    96  			defer func() {
    97  				a = 10
    98  			}()
    99  			a = 3
   100  			return a
   101  		}`
   102  		eval(t, src, big.NewInt(13))
   103  	})
   104  	t.Run("NoReturnReturn", func(t *testing.T) {
   105  		src := `package main
   106  		var i int
   107  		func Main() {
   108  			defer func() {
   109  				i++
   110  			}()
   111  			return
   112  		}`
   113  		vm := vmAndCompile(t, src)
   114  		err := vm.Run()
   115  		require.NoError(t, err)
   116  		require.Equal(t, 0, vm.Estack().Len(), "stack contains unexpected items")
   117  	})
   118  	t.Run("NoReturnNoReturn", func(t *testing.T) {
   119  		src := `package main
   120  		var i int
   121  		func Main() {
   122  			defer func() {
   123  				i++
   124  			}()
   125  		}`
   126  		vm := vmAndCompile(t, src)
   127  		err := vm.Run()
   128  		require.NoError(t, err)
   129  		require.Equal(t, 0, vm.Estack().Len(), "stack contains unexpected items")
   130  	})
   131  	t.Run("CodeDuplication", func(t *testing.T) {
   132  		src := `package main
   133  		var i int
   134  		func Main() {
   135  			defer func() {
   136  				var j int
   137  				i += j
   138  			}()
   139  			if i == 1 { return }
   140  			if i == 2 { return }
   141  			if i == 3 { return }
   142  			if i == 4 { return }
   143  			if i == 5 { return }
   144  		}`
   145  		checkCallCount(t, src, 0 /* defer body + Main */, 2, -1)
   146  	})
   147  }
   148  
   149  func TestConditionalDefer(t *testing.T) {
   150  	type testCase struct {
   151  		a      []bool
   152  		result int64
   153  	}
   154  
   155  	t.Run("no panic", func(t *testing.T) {
   156  		src := `package foo
   157  		var i int
   158  		func Main(a []bool) int { return f(a[0], a[1], a[2]) + i }
   159  		func g() { i += 10 }
   160  		func f(a bool, b bool, c bool) int {
   161  			if a { defer func() { i += 1 }() }
   162  			if b { defer g() }
   163  			if c { defer func() { i += 100 }() }
   164  			return 0
   165  		}`
   166  		testCases := []testCase{
   167  			{[]bool{false, false, false}, 0},
   168  			{[]bool{false, false, true}, 100},
   169  			{[]bool{false, true, false}, 10},
   170  			{[]bool{false, true, true}, 110},
   171  			{[]bool{true, false, false}, 1},
   172  			{[]bool{true, false, true}, 101},
   173  			{[]bool{true, true, false}, 11},
   174  			{[]bool{true, true, true}, 111},
   175  		}
   176  		for _, tc := range testCases {
   177  			args := []stackitem.Item{stackitem.Make(tc.a[0]), stackitem.Make(tc.a[1]), stackitem.Make(tc.a[2])}
   178  			evalWithArgs(t, src, nil, args, big.NewInt(tc.result))
   179  		}
   180  	})
   181  	t.Run("panic between ifs", func(t *testing.T) {
   182  		src := `package foo
   183  		var i int
   184  		func Main(a []bool) int { if a[1] { defer func() { recover() }() }; return f(a[0], a[1]) + i }
   185  		func f(a, b bool) int {
   186  			if a { defer func() { i += 1; recover() }() }
   187  			panic("totally expected")
   188  			if b { defer func() { i += 100; recover() }() }
   189  			return 0
   190  		}`
   191  
   192  		args := []stackitem.Item{stackitem.Make(false), stackitem.Make(false)}
   193  		v := vmAndCompile(t, src)
   194  		v.Estack().PushVal(args)
   195  		err := v.Run()
   196  		require.Error(t, err)
   197  		require.True(t, strings.Contains(err.Error(), "totally expected"))
   198  
   199  		testCases := []testCase{
   200  			{[]bool{false, true}, 0},
   201  			{[]bool{true, false}, 1},
   202  			{[]bool{true, true}, 1},
   203  		}
   204  		for _, tc := range testCases {
   205  			args := []stackitem.Item{stackitem.Make(tc.a[0]), stackitem.Make(tc.a[1])}
   206  			evalWithArgs(t, src, nil, args, big.NewInt(tc.result))
   207  		}
   208  	})
   209  	t.Run("panic in conditional", func(t *testing.T) {
   210  		src := `package foo
   211  		var i int
   212  		func Main(a []bool) int { if a[1] { defer func() { recover() }() }; return f(a[0], a[1]) + i }
   213  		func f(a, b bool) int {
   214  			if a {
   215  				defer func() { i += 1; recover() }()
   216  				panic("somewhat expected")
   217  			}
   218  			if b { defer func() { i += 100; recover() }() }
   219  			return 0
   220  		}`
   221  
   222  		testCases := []testCase{
   223  			{[]bool{false, false}, 0},
   224  			{[]bool{false, true}, 100},
   225  			{[]bool{true, false}, 1},
   226  			{[]bool{true, true}, 1},
   227  		}
   228  		for _, tc := range testCases {
   229  			args := []stackitem.Item{stackitem.Make(tc.a[0]), stackitem.Make(tc.a[1])}
   230  			evalWithArgs(t, src, nil, args, big.NewInt(tc.result))
   231  		}
   232  	})
   233  }
   234  
   235  func TestRecover(t *testing.T) {
   236  	t.Run("Panic", func(t *testing.T) {
   237  		src := `package foo
   238  		var a int
   239  		func Main() int {
   240  			return h() + a
   241  		}
   242  		func h() int {
   243  			defer func() {
   244  				if r := recover(); r != nil {
   245  					a = 3
   246  				} else {
   247  					a = 4
   248  				}
   249  			}()
   250  			a = 1
   251  			panic("msg")
   252  			return a
   253  		}`
   254  		eval(t, src, big.NewInt(3))
   255  	})
   256  	t.Run("NoPanic", func(t *testing.T) {
   257  		src := `package foo
   258  		var a int
   259  		func Main() int {
   260  			return h() + a
   261  		}
   262  		func h() int {
   263  			defer func() {
   264  				if r := recover(); r != nil {
   265  					a = 3
   266  				} else {
   267  					a = 4
   268  				}
   269  			}()
   270  			a = 1
   271  			return a
   272  		}`
   273  		eval(t, src, big.NewInt(5))
   274  	})
   275  	t.Run("PanicInDefer", func(t *testing.T) {
   276  		src := `package foo
   277  		var a int
   278  		func Main() int {
   279  			return h() + a
   280  		}
   281  		func h() int {
   282  			defer func() { a += 2; recover() }()
   283  			defer func() { a *= 3; recover(); panic("again") }()
   284  			a = 1
   285  			panic("msg")
   286  			return a
   287  		}`
   288  		eval(t, src, big.NewInt(5))
   289  	})
   290  }
   291  
   292  func TestDeferNoGlobals(t *testing.T) {
   293  	src := `package foo
   294  	func Main() int {
   295  		a := 1
   296  		defer func() { recover() }()
   297  		panic("msg")
   298  		return a
   299  	}`
   300  	eval(t, src, big.NewInt(0))
   301  }