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 }