github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/compiler/struct_test.go (about) 1 package compiler_test 2 3 import ( 4 "bytes" 5 "fmt" 6 "math/big" 7 "strings" 8 "testing" 9 10 "github.com/nspcc-dev/neo-go/pkg/compiler" 11 "github.com/nspcc-dev/neo-go/pkg/vm" 12 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 13 "github.com/stretchr/testify/require" 14 ) 15 16 var structTestCases = []testCase{ 17 { 18 "struct field assign", 19 `func F%d() int { 20 t := token1 { 21 x: 2, 22 y: 4, 23 } 24 25 age := t.x 26 return age 27 } 28 29 type token1 struct { 30 x int 31 y int 32 } 33 `, 34 big.NewInt(2), 35 }, 36 { 37 "struct field from func result", 38 `type S struct { x int } 39 func fn() int { return 2 } 40 func F%d() int { 41 t := S{x: fn()} 42 return t.x 43 } 44 `, 45 big.NewInt(2), 46 }, 47 { 48 "struct field return", 49 `type token2 struct { 50 x int 51 y int 52 } 53 54 func F%d() int { 55 t := token2 { 56 x: 2, 57 y: 4, 58 } 59 60 return t.x 61 } 62 `, 63 big.NewInt(2), 64 }, 65 { 66 "struct field assign", 67 `type token3 struct { 68 x int 69 y int 70 } 71 72 func F%d() int { 73 t := token3 { 74 x: 2, 75 y: 4, 76 } 77 t.x = 10 78 return t.x 79 } 80 `, 81 big.NewInt(10), 82 }, 83 { 84 "complex struct", 85 `type token4 struct { 86 x int 87 y int 88 } 89 90 func F%d() int { 91 x := 10 92 93 t := token4 { 94 x: 2, 95 y: 4, 96 } 97 98 y := x + t.x 99 100 return y 101 } 102 `, 103 big.NewInt(12), 104 }, 105 { 106 "initialize struct field from variable", 107 `type token5 struct { 108 x int 109 y int 110 } 111 112 func F%d() int { 113 x := 10 114 t := token5 { 115 x: x, 116 y: 4, 117 } 118 y := t.x + t.y 119 return y 120 } 121 `, 122 big.NewInt(14), 123 }, 124 { 125 "assign a variable to a struct field", 126 `type token6 struct { 127 x int 128 y int 129 } 130 131 func F%d() int { 132 ten := 10 133 t := token6 { 134 x: 2, 135 y: 4, 136 } 137 t.x = ten 138 y := t.y + t.x 139 return y 140 } 141 `, 142 big.NewInt(14), 143 }, 144 { 145 "increase struct field with +=", 146 `type token7 struct { x int } 147 func F%d() int { 148 t := token7{x: 2} 149 t.x += 3 150 return t.x 151 } 152 `, 153 big.NewInt(5), 154 }, 155 { 156 "assign a struct field to a struct field", 157 `type token8 struct { 158 x int 159 y int 160 } 161 162 func F%d() int { 163 t1 := token8 { 164 x: 2, 165 y: 4, 166 } 167 t2 := token8 { 168 x: 3, 169 y: 5, 170 } 171 t1.x = t2.y 172 y := t1.x + t2.x 173 return y 174 } 175 `, 176 big.NewInt(8), 177 }, 178 { 179 "initialize same struct twice", 180 `type token9 struct { 181 x int 182 y int 183 } 184 185 func F%d() int { 186 t1 := token9 { 187 x: 2, 188 y: 4, 189 } 190 t2 := token9 { 191 x: 2, 192 y: 4, 193 } 194 return t1.x + t2.y 195 } 196 `, 197 big.NewInt(6), 198 }, 199 { 200 "struct methods", 201 `type token10 struct { 202 x int 203 } 204 205 func(t token10) getInteger() int { 206 return t.x 207 } 208 209 func F%d() int { 210 t := token10 { 211 x: 4, 212 } 213 someInt := t.getInteger() 214 return someInt 215 } 216 `, 217 big.NewInt(4), 218 }, 219 { 220 "struct methods with arguments", 221 `type token11 struct { 222 x int 223 } 224 225 // Also tests if x conflicts with t.x 226 func(t token11) addIntegers(x int, y int) int { 227 return t.x + x + y 228 } 229 230 func F%d() int { 231 t := token11 { 232 x: 4, 233 } 234 someInt := t.addIntegers(2, 4) 235 return someInt 236 } 237 `, 238 big.NewInt(10), 239 }, 240 { 241 "initialize struct partially", 242 `type token12 struct { 243 x int 244 y int 245 z string 246 b bool 247 } 248 249 func F%d() int { 250 t := token12 { 251 x: 4, 252 } 253 return t.y 254 } 255 `, 256 big.NewInt(0), 257 }, 258 { 259 "test return struct from func", 260 `type token13 struct { 261 x int 262 y int 263 z string 264 b bool 265 } 266 267 func newToken() token13 { 268 return token13{ 269 x: 1, 270 y: 2, 271 z: "hello", 272 b: false, 273 } 274 } 275 276 func F%d() token13 { 277 return newToken() 278 } 279 `, 280 []stackitem.Item{ 281 stackitem.NewBigInteger(big.NewInt(1)), 282 stackitem.NewBigInteger(big.NewInt(2)), 283 stackitem.NewByteArray([]byte("hello")), 284 stackitem.NewBool(false), 285 }, 286 }, 287 { 288 "pass struct as argument", 289 `type Bar struct { 290 amount int 291 } 292 293 func addToAmount(x int, bar Bar) int { 294 bar.amount = bar.amount + x 295 return bar.amount 296 } 297 298 func F%d() int { 299 b := Bar{ 300 amount: 10, 301 } 302 303 x := addToAmount(4, b) 304 return x 305 } 306 `, 307 big.NewInt(14), 308 }, 309 { 310 "declare struct literal", 311 `func F%d() int { 312 var x struct { 313 a int 314 } 315 x.a = 2 316 return x.a 317 } 318 `, 319 big.NewInt(2), 320 }, 321 { 322 "declare struct type", 323 `type withA struct { 324 a int 325 } 326 func F%d() int { 327 var x withA 328 x.a = 2 329 return x.a 330 } 331 `, 332 big.NewInt(2), 333 }, 334 { 335 "nested selectors (simple read)", 336 `type S1 struct { x, y S2 } 337 type S2 struct { a, b int } 338 func F%d() int { 339 var s1 S1 340 var s2 S2 341 s2.a = 3 342 s1.y = s2 343 return s1.y.a 344 } 345 `, 346 big.NewInt(3), 347 }, 348 { 349 "nested selectors (simple write)", 350 `type S3 struct { x S4 } 351 type S4 struct { a int } 352 func F%d() int { 353 s1 := S3{ 354 x: S4 { 355 a: 3, 356 }, 357 } 358 s1.x.a = 11 359 return s1.x.a 360 } 361 `, 362 big.NewInt(11), 363 }, 364 { 365 "complex struct default value", 366 `type S5 struct { x S6 } 367 type S6 struct { y S7 } 368 type S7 struct { a int } 369 func F%d() int { 370 var s1 S5 371 s1.x.y.a = 11 372 return s1.x.y.a 373 } 374 `, 375 big.NewInt(11), 376 }, 377 { 378 "lengthy struct default value", 379 `type SS struct { x int; y []byte; z bool } 380 func F%d() int { 381 var s SS 382 return s.x 383 } 384 `, 385 big.NewInt(0), 386 }, 387 { 388 "nested selectors (complex write)", 389 `type S8 struct { x S9 } 390 type S9 struct { y, z S10 } 391 type S10 struct { a int } 392 func F%d() int { 393 var s1 S8 394 s1.x.y.a, s1.x.z.a = 11, 31 395 return s1.x.y.a + s1.x.z.a 396 } 397 `, 398 big.NewInt(42), 399 }, 400 { 401 "omit field names", 402 `type pair struct { a, b int } 403 func F%d() int { 404 p := pair{1, 2} 405 x := p.a * 10 406 return x + p.b 407 } 408 `, 409 big.NewInt(12), 410 }, 411 { 412 "uninitialized struct fields", 413 `type Foo struct { 414 i int 415 m map[string]int 416 b []byte 417 a []int 418 s struct { ii int } 419 } 420 func NewFoo() Foo { return Foo{} } 421 func F%d() int { 422 foo := NewFoo() 423 if foo.i != 0 { return 1 } 424 if len(foo.m) != 0 { return 1 } 425 if len(foo.b) != 0 { return 1 } 426 if len(foo.a) != 0 { return 1 } 427 s := foo.s 428 if s.ii != 0 { return 1 } 429 return 2 430 } 431 `, 432 big.NewInt(2), 433 }, 434 } 435 436 func TestStructs(t *testing.T) { 437 srcBuilder := bytes.NewBuffer([]byte("package testcase\n")) 438 for i, tc := range structTestCases { 439 srcBuilder.WriteString(fmt.Sprintf(tc.src, i)) 440 } 441 442 ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil) 443 require.NoError(t, err) 444 445 for i, tc := range structTestCases { 446 t.Run(tc.name, func(t *testing.T) { 447 v := vm.New() 448 invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di) 449 runAndCheck(t, v, tc.result) 450 }) 451 } 452 }