github.com/gotranspile/cxgo@v0.3.8-0.20240118201721-29871598a6a2/flow_test.go (about) 1 package cxgo 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "strings" 10 "testing" 11 12 "github.com/stretchr/testify/require" 13 14 "github.com/gotranspile/cxgo/libs" 15 "github.com/gotranspile/cxgo/types" 16 ) 17 18 func numStmt(n int) CStmt { 19 return NewCExprStmt1(&CallExpr{ 20 Fun: FuncIdent{types.NewIdent("foo", types.UnkT(1))}, 21 Args: []Expr{cIntLit(int64(n), 10)}, 22 }) 23 } 24 25 func varDecl(n int) CStmt { 26 return &CDeclStmt{&CVarDecl{ 27 CVarSpec: CVarSpec{ 28 Type: types.NamedT("bar", types.UnkT(1)), 29 Names: []*types.Ident{ 30 types.NewIdent(fmt.Sprintf("foo%d", n), types.UnkT(1)), 31 }, 32 Inits: []Expr{ 33 cIntLit(int64(n), 10), 34 }, 35 }, 36 }} 37 } 38 39 func numCond(n int) BoolExpr { 40 return BoolIdent{types.NewIdent(fmt.Sprintf("%d", n), types.BoolT())} 41 } 42 43 func ret(n int) CStmt { 44 return &CReturnStmt{Expr: cIntLit(int64(n), 10)} 45 } 46 47 func newBlock(stmts ...CStmt) *BlockStmt { 48 return &BlockStmt{Stmts: stmts} 49 } 50 51 var casesControlFlow = []struct { 52 name string 53 tree []CStmt 54 exp string 55 dom string 56 flat string 57 }{ 58 { 59 name: "return", 60 tree: []CStmt{ 61 ret(1), 62 }, 63 exp: ` 64 n2[label="return 1",shape="box"]; 65 n1->n2; 66 `, 67 dom: ` 68 n2[label="return 1",shape="box"]; 69 n1->n2; 70 `, 71 flat: ` 72 return 1 73 `, 74 }, 75 { 76 name: "no return", 77 tree: []CStmt{ 78 numStmt(1), 79 }, 80 exp: ` 81 n2[label="foo(1)",shape="box"]; 82 n3[label="return",shape="box"]; 83 n1->n2; 84 n2->n3; 85 n3->n2[color="#99999955"]; 86 `, 87 dom: ` 88 n2[label="foo(1)",shape="box"]; 89 n3[label="return",shape="box"]; 90 n1->n2; 91 n2->n3; 92 `, 93 flat: ` 94 foo(1) 95 goto L_1 96 L_1: 97 return 98 `, 99 }, 100 { 101 name: "code and return", 102 tree: []CStmt{ 103 numStmt(1), 104 ret(2), 105 }, 106 exp: ` 107 n2[label="foo(1)",shape="box"]; 108 n3[label="return 2",shape="box"]; 109 n1->n2; 110 n2->n3; 111 n3->n2[color="#99999955"]; 112 `, 113 dom: ` 114 n2[label="foo(1)",shape="box"]; 115 n3[label="return 2",shape="box"]; 116 n1->n2; 117 n2->n3; 118 `, 119 flat: ` 120 foo(1) 121 goto L_1 122 L_1: 123 return 2 124 `, 125 }, 126 { 127 name: "code and return 2", 128 tree: []CStmt{ 129 numStmt(1), 130 numStmt(2), 131 ret(3), 132 }, 133 exp: ` 134 n2[label="foo(1)\nfoo(2)",shape="box"]; 135 n3[label="return 3",shape="box"]; 136 n1->n2; 137 n2->n3; 138 n3->n2[color="#99999955"]; 139 `, 140 dom: ` 141 n2[label="foo(1)\nfoo(2)",shape="box"]; 142 n3[label="return 3",shape="box"]; 143 n1->n2; 144 n2->n3; 145 `, 146 flat: ` 147 foo(1) 148 foo(2) 149 goto L_1 150 L_1: 151 return 3 152 `, 153 }, 154 { 155 name: "code with decls", 156 tree: []CStmt{ 157 &CIfStmt{Cond: numCond(1), Then: newBlock(varDecl(1))}, 158 varDecl(2), 159 ret(1), 160 }, 161 exp: ` 162 n2[label="if 1",shape="hexagon"]; 163 n3[label="var foo1 bar = 1",shape="box"]; 164 n4[label="var foo2 bar = 2",shape="box"]; 165 n5[label="return 1",shape="box"]; 166 n1->n2; 167 n2->n3[color="#00aa00"]; 168 n2->n4[color="#aa0000"]; 169 n3->n2[color="#99999955"]; 170 n3->n4; 171 n4->n3[color="#99999955"]; 172 n4->n2[color="#99999955"]; 173 n4->n5; 174 n5->n4[color="#99999955"]; 175 `, 176 dom: ` 177 n2[label="if 1",shape="hexagon"]; 178 n3[label="var foo1 bar = 1",shape="box"]; 179 n4[label="var foo2 bar = 2",shape="box"]; 180 n5[label="return 1",shape="box"]; 181 n1->n2; 182 n2->n3; 183 n2->n4; 184 n4->n5; 185 `, 186 flat: ` 187 var foo1 bar 188 var foo2 bar 189 if 1 { 190 goto L_1 191 } else { 192 goto L_2 193 } 194 L_1: 195 foo1 = 1 196 goto L_2 197 L_2: 198 foo2 = 2 199 goto L_3 200 L_3: 201 return 1 202 `, 203 }, 204 { 205 name: "if", 206 tree: []CStmt{ 207 &CIfStmt{Cond: numCond(1), Then: newBlock(numStmt(2))}, 208 numStmt(3), 209 numStmt(4), 210 ret(5), 211 }, 212 exp: ` 213 n2[label="if 1",shape="hexagon"]; 214 n3[label="foo(2)",shape="box"]; 215 n4[label="foo(3)\nfoo(4)",shape="box"]; 216 n5[label="return 5",shape="box"]; 217 n1->n2; 218 n2->n3[color="#00aa00"]; 219 n2->n4[color="#aa0000"]; 220 n3->n2[color="#99999955"]; 221 n3->n4; 222 n4->n3[color="#99999955"]; 223 n4->n2[color="#99999955"]; 224 n4->n5; 225 n5->n4[color="#99999955"]; 226 `, 227 dom: ` 228 n2[label="if 1",shape="hexagon"]; 229 n3[label="foo(2)",shape="box"]; 230 n4[label="foo(3)\nfoo(4)",shape="box"]; 231 n5[label="return 5",shape="box"]; 232 n1->n2; 233 n2->n3; 234 n2->n4; 235 n4->n5; 236 `, 237 flat: ` 238 if 1 { 239 goto L_1 240 } else { 241 goto L_2 242 } 243 L_1: 244 foo(2) 245 goto L_2 246 L_2: 247 foo(3) 248 foo(4) 249 goto L_3 250 L_3: 251 return 5 252 `, 253 }, 254 { 255 name: "if else", 256 tree: []CStmt{ 257 &CIfStmt{Cond: numCond(1), Then: newBlock(numStmt(2)), Else: newBlock(numStmt(3))}, 258 ret(4), 259 }, 260 exp: ` 261 n2[label="if 1",shape="hexagon"]; 262 n3[label="foo(2)",shape="box"]; 263 n4[label="return 4",shape="box"]; 264 n5[label="foo(3)",shape="box"]; 265 n1->n2; 266 n2->n3[color="#00aa00"]; 267 n2->n5[color="#aa0000"]; 268 n3->n2[color="#99999955"]; 269 n3->n4; 270 n4->n3[color="#99999955"]; 271 n4->n5[color="#99999955"]; 272 n5->n2[color="#99999955"]; 273 n5->n4; 274 `, 275 dom: ` 276 n2[label="if 1",shape="hexagon"]; 277 n3[label="foo(2)",shape="box"]; 278 n4[label="return 4",shape="box"]; 279 n5[label="foo(3)",shape="box"]; 280 n1->n2; 281 n2->n3; 282 n2->n4; 283 n2->n5; 284 `, 285 flat: ` 286 if 1 { 287 goto L_1 288 } else { 289 goto L_3 290 } 291 L_1: 292 foo(2) 293 goto L_2 294 L_2: 295 return 4 296 L_3: 297 foo(3) 298 goto L_2 299 `, 300 }, 301 { 302 name: "if return else", 303 tree: []CStmt{ 304 &CIfStmt{Cond: numCond(1), Then: newBlock(ret(2)), Else: newBlock(numStmt(3))}, 305 ret(4), 306 }, 307 exp: ` 308 n2[label="if 1",shape="hexagon"]; 309 n3[label="return 2",shape="box"]; 310 n4[label="foo(3)",shape="box"]; 311 n5[label="return 4",shape="box"]; 312 n1->n2; 313 n2->n3[color="#00aa00"]; 314 n2->n4[color="#aa0000"]; 315 n3->n2[color="#99999955"]; 316 n4->n2[color="#99999955"]; 317 n4->n5; 318 n5->n4[color="#99999955"]; 319 `, 320 dom: ` 321 n2[label="if 1",shape="hexagon"]; 322 n3[label="return 2",shape="box"]; 323 n4[label="foo(3)",shape="box"]; 324 n5[label="return 4",shape="box"]; 325 n1->n2; 326 n2->n3; 327 n2->n4; 328 n4->n5; 329 `, 330 flat: ` 331 if 1 { 332 goto L_1 333 } else { 334 goto L_2 335 } 336 L_1: 337 return 2 338 L_2: 339 foo(3) 340 goto L_3 341 L_3: 342 return 4 343 `, 344 }, 345 { 346 name: "if else return", 347 tree: []CStmt{ 348 &CIfStmt{Cond: numCond(1), Then: newBlock(numStmt(2)), Else: newBlock(ret(3))}, 349 ret(4), 350 }, 351 exp: ` 352 n2[label="if 1",shape="hexagon"]; 353 n3[label="foo(2)",shape="box"]; 354 n4[label="return 4",shape="box"]; 355 n5[label="return 3",shape="box"]; 356 n1->n2; 357 n2->n3[color="#00aa00"]; 358 n2->n5[color="#aa0000"]; 359 n3->n2[color="#99999955"]; 360 n3->n4; 361 n4->n3[color="#99999955"]; 362 n5->n2[color="#99999955"]; 363 `, 364 dom: ` 365 n2[label="if 1",shape="hexagon"]; 366 n3[label="foo(2)",shape="box"]; 367 n4[label="return 4",shape="box"]; 368 n5[label="return 3",shape="box"]; 369 n1->n2; 370 n2->n3; 371 n2->n5; 372 n3->n4; 373 `, 374 flat: ` 375 if 1 { 376 goto L_1 377 } else { 378 goto L_3 379 } 380 L_1: 381 foo(2) 382 goto L_2 383 L_2: 384 return 4 385 L_3: 386 return 3 387 `, 388 }, 389 { 390 name: "if all return", 391 tree: []CStmt{ 392 &CIfStmt{Cond: numCond(1), Then: newBlock(ret(2)), Else: newBlock(ret(3))}, 393 }, 394 exp: ` 395 n2[label="if 1",shape="hexagon"]; 396 n3[label="return 2",shape="box"]; 397 n4[label="return 3",shape="box"]; 398 n1->n2; 399 n2->n3[color="#00aa00"]; 400 n2->n4[color="#aa0000"]; 401 n3->n2[color="#99999955"]; 402 n4->n2[color="#99999955"]; 403 `, 404 dom: ` 405 n2[label="if 1",shape="hexagon"]; 406 n3[label="return 2",shape="box"]; 407 n4[label="return 3",shape="box"]; 408 n1->n2; 409 n2->n3; 410 n2->n4; 411 `, 412 flat: ` 413 if 1 { 414 goto L_1 415 } else { 416 goto L_2 417 } 418 L_1: 419 return 2 420 L_2: 421 return 3 422 `, 423 }, 424 { 425 name: "if all return unreachable", 426 tree: []CStmt{ 427 &CIfStmt{Cond: numCond(1), Then: newBlock(ret(2)), Else: newBlock(ret(3))}, 428 ret(4), 429 }, 430 exp: ` 431 n2[label="if 1",shape="hexagon"]; 432 n3[label="return 2",shape="box"]; 433 n4[label="return 3",shape="box"]; 434 n1->n2; 435 n2->n3[color="#00aa00"]; 436 n2->n4[color="#aa0000"]; 437 n3->n2[color="#99999955"]; 438 n4->n2[color="#99999955"]; 439 `, 440 dom: ` 441 n2[label="if 1",shape="hexagon"]; 442 n3[label="return 2",shape="box"]; 443 n4[label="return 3",shape="box"]; 444 n1->n2; 445 n2->n3; 446 n2->n4; 447 `, 448 flat: ` 449 if 1 { 450 goto L_1 451 } else { 452 goto L_2 453 } 454 L_1: 455 return 2 456 L_2: 457 return 3 458 `, 459 }, 460 { 461 name: "switch", 462 tree: []CStmt{ 463 &CSwitchStmt{ 464 Cond: cIntLit(1, 10), 465 Cases: []*CCaseStmt{ 466 {Expr: cIntLit(1, 10), Stmts: []CStmt{numStmt(1)}}, 467 {Expr: cIntLit(2, 10), Stmts: []CStmt{numStmt(2)}}, 468 {Expr: cIntLit(3, 10), Stmts: []CStmt{numStmt(3), &CBreakStmt{}}}, 469 {Stmts: []CStmt{numStmt(4)}}, 470 }, 471 }, 472 ret(1), 473 }, 474 exp: ` 475 n2[label="switch 1",shape="trapezium"]; 476 n3[label="foo(1)",shape="box"]; 477 n4[label="foo(2)",shape="box"]; 478 n5[label="foo(3)",shape="box"]; 479 n6[label="return 1",shape="box"]; 480 n7[label="foo(4)",shape="box"]; 481 n1->n2; 482 n2->n3[label="1"]; 483 n2->n4[label="2"]; 484 n2->n5[label="3"]; 485 n2->n7[color="#aa0000"]; 486 n3->n2[color="#99999955"]; 487 n3->n4; 488 n4->n2[color="#99999955"]; 489 n4->n3[color="#99999955"]; 490 n4->n5; 491 n5->n2[color="#99999955"]; 492 n5->n4[color="#99999955"]; 493 n5->n6; 494 n6->n7[color="#99999955"]; 495 n6->n5[color="#99999955"]; 496 n7->n2[color="#99999955"]; 497 n7->n6; 498 `, 499 dom: ` 500 n2[label="switch 1",shape="trapezium"]; 501 n3[label="foo(1)",shape="box"]; 502 n4[label="foo(2)",shape="box"]; 503 n5[label="foo(3)",shape="box"]; 504 n6[label="return 1",shape="box"]; 505 n7[label="foo(4)",shape="box"]; 506 n1->n2; 507 n2->n3; 508 n2->n4; 509 n2->n5; 510 n2->n6; 511 n2->n7; 512 `, 513 flat: ` 514 switch 1 { 515 case 1: 516 goto L_1 517 case 2: 518 goto L_2 519 case 3: 520 goto L_3 521 default: 522 goto L_5 523 } 524 L_1: 525 foo(1) 526 goto L_2 527 L_2: 528 foo(2) 529 goto L_3 530 L_3: 531 foo(3) 532 goto L_4 533 L_4: 534 return 1 535 L_5: 536 foo(4) 537 goto L_4 538 `, 539 }, 540 { 541 name: "switch no default", 542 tree: []CStmt{ 543 &CSwitchStmt{ 544 Cond: cIntLit(1, 10), 545 Cases: []*CCaseStmt{ 546 {Expr: cIntLit(1, 10), Stmts: []CStmt{numStmt(1)}}, 547 {Expr: cIntLit(2, 10), Stmts: []CStmt{&CBreakStmt{}}}, 548 {Expr: cIntLit(3, 10), Stmts: []CStmt{numStmt(3), &CBreakStmt{}}}, 549 }, 550 }, 551 ret(1), 552 }, 553 exp: ` 554 n2[label="switch 1",shape="trapezium"]; 555 n3[label="foo(1)",shape="box"]; 556 n4[label="return 1",shape="box"]; 557 n5[label="foo(3)",shape="box"]; 558 n1->n2; 559 n2->n3[label="1"]; 560 n2->n4[label="2"]; 561 n2->n5[label="3"]; 562 n2->n4[color="#aa0000"]; 563 n3->n2[color="#99999955"]; 564 n3->n4; 565 n4->n5[color="#99999955"]; 566 n4->n2[color="#99999955"]; 567 n4->n3[color="#99999955"]; 568 n5->n2[color="#99999955"]; 569 n5->n4; 570 `, 571 dom: ` 572 n2[label="switch 1",shape="trapezium"]; 573 n3[label="foo(1)",shape="box"]; 574 n4[label="return 1",shape="box"]; 575 n5[label="foo(3)",shape="box"]; 576 n1->n2; 577 n2->n3; 578 n2->n4; 579 n2->n5; 580 `, 581 flat: ` 582 switch 1 { 583 case 1: 584 goto L_1 585 case 2: 586 goto L_2 587 case 3: 588 goto L_3 589 default: 590 goto L_2 591 } 592 L_1: 593 foo(1) 594 goto L_2 595 L_2: 596 return 1 597 L_3: 598 foo(3) 599 goto L_2 600 `, 601 }, 602 { 603 name: "goto", 604 tree: []CStmt{ 605 numStmt(1), 606 &CIfStmt{ 607 Cond: numCond(1), 608 Then: newBlock( 609 numStmt(2), 610 &CGotoStmt{Label: "L1"}, 611 ), 612 }, 613 &CLabelStmt{Label: "L1"}, 614 ret(3), 615 }, 616 exp: ` 617 n2[label="foo(1)",shape="box"]; 618 n3[label="if 1",shape="hexagon"]; 619 n4[label="foo(2)",shape="box"]; 620 n5[label="return 3",shape="box"]; 621 n1->n2; 622 n2->n3; 623 n3->n2[color="#99999955"]; 624 n3->n4[color="#00aa00"]; 625 n3->n5[color="#aa0000"]; 626 n4->n3[color="#99999955"]; 627 n4->n5; 628 n5->n4[color="#99999955"]; 629 n5->n3[color="#99999955"]; 630 `, 631 dom: ` 632 n2[label="foo(1)",shape="box"]; 633 n3[label="if 1",shape="hexagon"]; 634 n4[label="foo(2)",shape="box"]; 635 n5[label="return 3",shape="box"]; 636 n1->n2; 637 n2->n3; 638 n3->n4; 639 n3->n5; 640 `, 641 flat: ` 642 foo(1) 643 goto L_1 644 L_1: 645 if 1 { 646 goto L_2 647 } else { 648 goto L_3 649 } 650 L_2: 651 foo(2) 652 goto L_3 653 L_3: 654 return 3 655 `, 656 }, 657 { 658 name: "goto loop", 659 tree: []CStmt{ 660 numStmt(0), 661 &CLabelStmt{Label: "L1"}, 662 &CIfStmt{ 663 Cond: numCond(1), 664 Then: newBlock( 665 numStmt(1), 666 &CGotoStmt{Label: "L1"}, 667 ), 668 }, 669 ret(3), 670 }, 671 exp: ` 672 n2[label="foo(0)",shape="box"]; 673 n3[label="if 1",shape="hexagon"]; 674 n4[label="foo(1)",shape="box"]; 675 n5[label="return 3",shape="box"]; 676 n1->n2; 677 n2->n3; 678 n3->n4[color="#99999955"]; 679 n3->n2[color="#99999955"]; 680 n3->n4[color="#00aa00"]; 681 n3->n5[color="#aa0000"]; 682 n4->n3[color="#99999955"]; 683 n4->n3; 684 n5->n3[color="#99999955"]; 685 `, 686 dom: ` 687 n2[label="foo(0)",shape="box"]; 688 n3[label="if 1",shape="hexagon"]; 689 n4[label="foo(1)",shape="box"]; 690 n5[label="return 3",shape="box"]; 691 n1->n2; 692 n2->n3; 693 n3->n4; 694 n3->n5; 695 `, 696 flat: ` 697 foo(0) 698 goto L_1 699 L_1: 700 if 1 { 701 goto L_2 702 } else { 703 goto L_3 704 } 705 L_2: 706 foo(1) 707 goto L_1 708 L_3: 709 return 3 710 `, 711 }, 712 { 713 name: "for infinite empty", 714 tree: []CStmt{ 715 &CForStmt{}, 716 ret(2), 717 }, 718 exp: ` 719 n2[label="",shape="box"]; 720 n1->n2; 721 n2->n2[color="#99999955"]; 722 n2->n2; 723 `, 724 dom: ` 725 n2[label="",shape="box"]; 726 n1->n2; 727 `, 728 flat: ` 729 L_1: 730 goto L_1 731 `, 732 }, 733 { 734 name: "for infinite", 735 tree: []CStmt{ 736 &CForStmt{Body: *newBlock(numStmt(1))}, 737 ret(2), 738 }, 739 exp: ` 740 n2[label="foo(1)",shape="box"]; 741 n1->n2; 742 n2->n2[color="#99999955"]; 743 n2->n2; 744 `, 745 dom: ` 746 n2[label="foo(1)",shape="box"]; 747 n1->n2; 748 `, 749 flat: ` 750 L_1: 751 foo(1) 752 goto L_1 753 `, 754 }, 755 { 756 name: "for infinite break", 757 tree: []CStmt{ 758 &CForStmt{Body: *newBlock( 759 numStmt(1), 760 &CBreakStmt{}, 761 )}, 762 ret(2), 763 }, 764 exp: ` 765 n2[label="foo(1)",shape="box"]; 766 n3[label="return 2",shape="box"]; 767 n1->n2; 768 n2->n3; 769 n3->n2[color="#99999955"]; 770 `, 771 dom: ` 772 n2[label="foo(1)",shape="box"]; 773 n3[label="return 2",shape="box"]; 774 n1->n2; 775 n2->n3; 776 `, 777 flat: ` 778 foo(1) 779 goto L_1 780 L_1: 781 return 2 782 `, 783 }, 784 { 785 name: "for infinite continue", 786 tree: []CStmt{ 787 &CForStmt{Body: *newBlock( 788 numStmt(1), 789 &CContinueStmt{}, 790 )}, 791 ret(2), 792 }, 793 exp: ` 794 n2[label="foo(1)",shape="box"]; 795 n1->n2; 796 n2->n2[color="#99999955"]; 797 n2->n2; 798 `, 799 dom: ` 800 n2[label="foo(1)",shape="box"]; 801 n1->n2; 802 `, 803 flat: ` 804 L_1: 805 foo(1) 806 goto L_1 807 `, 808 }, 809 { 810 name: "for cond", 811 tree: []CStmt{ 812 &CForStmt{Cond: cIntLit(1, 10), Body: *newBlock( 813 numStmt(1), 814 )}, 815 ret(2), 816 }, 817 exp: ` 818 n2[label="if false",shape="hexagon"]; 819 n3[label="foo(1)",shape="box"]; 820 n4[label="return 2",shape="box"]; 821 n1->n2; 822 n2->n3[color="#99999955"]; 823 n2->n4[color="#00aa00"]; 824 n2->n3[color="#aa0000"]; 825 n3->n2[color="#99999955"]; 826 n3->n2; 827 n4->n2[color="#99999955"]; 828 `, 829 dom: ` 830 n2[label="if false",shape="hexagon"]; 831 n3[label="return 2",shape="box"]; 832 n4[label="foo(1)",shape="box"]; 833 n1->n2; 834 n2->n3; 835 n2->n4; 836 `, 837 flat: ` 838 L_1: 839 if false { 840 goto L_2 841 } else { 842 goto L_3 843 } 844 L_2: 845 return 2 846 L_3: 847 foo(1) 848 goto L_1 849 `, 850 }, 851 { 852 name: "for cond break", 853 tree: []CStmt{ 854 &CForStmt{ 855 Cond: cIntLit(1, 10), 856 Body: *newBlock( 857 &CIfStmt{ 858 Cond: numCond(2), 859 Then: newBlock( 860 numStmt(3), 861 &CBreakStmt{}, 862 ), 863 }, 864 numStmt(4), 865 ), 866 }, 867 ret(5), 868 }, 869 exp: ` 870 n2[label="if false",shape="hexagon"]; 871 n3[label="foo(4)",shape="box"]; 872 n4[label="if 2",shape="hexagon"]; 873 n5[label="foo(3)",shape="box"]; 874 n6[label="return 5",shape="box"]; 875 n1->n2; 876 n2->n3[color="#99999955"]; 877 n2->n6[color="#00aa00"]; 878 n2->n4[color="#aa0000"]; 879 n3->n4[color="#99999955"]; 880 n3->n2; 881 n4->n2[color="#99999955"]; 882 n4->n5[color="#00aa00"]; 883 n4->n3[color="#aa0000"]; 884 n5->n4[color="#99999955"]; 885 n5->n6; 886 n6->n5[color="#99999955"]; 887 n6->n2[color="#99999955"]; 888 `, 889 dom: ` 890 n2[label="if false",shape="hexagon"]; 891 n3[label="return 5",shape="box"]; 892 n4[label="if 2",shape="hexagon"]; 893 n5[label="foo(3)",shape="box"]; 894 n6[label="foo(4)",shape="box"]; 895 n1->n2; 896 n2->n3; 897 n2->n4; 898 n4->n5; 899 n4->n6; 900 `, 901 flat: ` 902 L_1: 903 if false { 904 goto L_2 905 } else { 906 goto L_3 907 } 908 L_2: 909 return 5 910 L_3: 911 if 2 { 912 goto L_4 913 } else { 914 goto L_5 915 } 916 L_4: 917 foo(3) 918 goto L_2 919 L_5: 920 foo(4) 921 goto L_1 922 `, 923 }, 924 { 925 name: "for cond nested", 926 tree: []CStmt{ 927 &CForStmt{Cond: cIntLit(2, 10), Body: *newBlock( 928 numStmt(2), 929 &CForStmt{Cond: cIntLit(3, 10), Body: *newBlock( 930 numStmt(3), 931 )}, 932 )}, 933 ret(1), 934 }, 935 exp: ` 936 n2[label="if 2 == 0",shape="hexagon"]; 937 n3[label="if 3 == 0",shape="hexagon"]; 938 n4[label="foo(3)",shape="box"]; 939 n5[label="foo(2)",shape="box"]; 940 n6[label="return 1",shape="box"]; 941 n1->n2; 942 n2->n3[color="#99999955"]; 943 n2->n6[color="#00aa00"]; 944 n2->n5[color="#aa0000"]; 945 n3->n4[color="#99999955"]; 946 n3->n5[color="#99999955"]; 947 n3->n2[color="#00aa00"]; 948 n3->n4[color="#aa0000"]; 949 n4->n3[color="#99999955"]; 950 n4->n3; 951 n5->n2[color="#99999955"]; 952 n5->n3; 953 n6->n2[color="#99999955"]; 954 `, 955 dom: ` 956 n2[label="if 2 == 0",shape="hexagon"]; 957 n3[label="return 1",shape="box"]; 958 n4[label="foo(2)",shape="box"]; 959 n5[label="if 3 == 0",shape="hexagon"]; 960 n6[label="foo(3)",shape="box"]; 961 n1->n2; 962 n2->n3; 963 n2->n4; 964 n4->n5; 965 n5->n6; 966 `, 967 flat: ` 968 L_1: 969 if 2 == 0 { 970 goto L_2 971 } else { 972 goto L_3 973 } 974 L_2: 975 return 1 976 L_3: 977 foo(2) 978 goto L_4 979 L_4: 980 if 3 == 0 { 981 goto L_1 982 } else { 983 goto L_5 984 } 985 L_5: 986 foo(3) 987 goto L_4 988 `, 989 }, 990 { 991 name: "for cond nested 2", 992 tree: []CStmt{ 993 &CForStmt{Cond: cIntLit(2, 10), Body: *newBlock( 994 numStmt(2), 995 &CForStmt{Cond: cIntLit(3, 10), Body: *newBlock( 996 numStmt(3), 997 )}, 998 numStmt(4), 999 )}, 1000 ret(1), 1001 }, 1002 exp: ` 1003 n2[label="if 2 == 0",shape="hexagon"]; 1004 n3[label="foo(4)",shape="box"]; 1005 n4[label="if 3 == 0",shape="hexagon"]; 1006 n5[label="foo(3)",shape="box"]; 1007 n6[label="foo(2)",shape="box"]; 1008 n7[label="return 1",shape="box"]; 1009 n1->n2; 1010 n2->n3[color="#99999955"]; 1011 n2->n7[color="#00aa00"]; 1012 n2->n6[color="#aa0000"]; 1013 n3->n4[color="#99999955"]; 1014 n3->n2; 1015 n4->n5[color="#99999955"]; 1016 n4->n6[color="#99999955"]; 1017 n4->n3[color="#00aa00"]; 1018 n4->n5[color="#aa0000"]; 1019 n5->n4[color="#99999955"]; 1020 n5->n4; 1021 n6->n2[color="#99999955"]; 1022 n6->n4; 1023 n7->n2[color="#99999955"]; 1024 `, 1025 dom: ` 1026 n2[label="if 2 == 0",shape="hexagon"]; 1027 n3[label="return 1",shape="box"]; 1028 n4[label="foo(2)",shape="box"]; 1029 n5[label="if 3 == 0",shape="hexagon"]; 1030 n6[label="foo(4)",shape="box"]; 1031 n7[label="foo(3)",shape="box"]; 1032 n1->n2; 1033 n2->n3; 1034 n2->n4; 1035 n4->n5; 1036 n5->n6; 1037 n5->n7; 1038 `, 1039 flat: ` 1040 L_1: 1041 if 2 == 0 { 1042 goto L_2 1043 } else { 1044 goto L_3 1045 } 1046 L_2: 1047 return 1 1048 L_3: 1049 foo(2) 1050 goto L_4 1051 L_4: 1052 if 3 == 0 { 1053 goto L_5 1054 } else { 1055 goto L_6 1056 } 1057 L_5: 1058 foo(4) 1059 goto L_1 1060 L_6: 1061 foo(3) 1062 goto L_4 1063 `, 1064 }, 1065 { 1066 name: "for full", 1067 tree: []CStmt{ 1068 &CForStmt{ 1069 Init: numStmt(1), Cond: cIntLit(1, 10), Iter: numStmt(2), 1070 Body: *newBlock( 1071 numStmt(3), 1072 ), 1073 }, 1074 ret(4), 1075 }, 1076 exp: ` 1077 n2[label="foo(1)",shape="box"]; 1078 n3[label="if false",shape="hexagon"]; 1079 n4[label="foo(2)",shape="box"]; 1080 n5[label="foo(3)",shape="box"]; 1081 n6[label="return 4",shape="box"]; 1082 n1->n2; 1083 n2->n3; 1084 n3->n4[color="#99999955"]; 1085 n3->n2[color="#99999955"]; 1086 n3->n6[color="#00aa00"]; 1087 n3->n5[color="#aa0000"]; 1088 n4->n5[color="#99999955"]; 1089 n4->n3; 1090 n5->n3[color="#99999955"]; 1091 n5->n4; 1092 n6->n3[color="#99999955"]; 1093 `, 1094 dom: ` 1095 n2[label="foo(1)",shape="box"]; 1096 n3[label="if false",shape="hexagon"]; 1097 n4[label="return 4",shape="box"]; 1098 n5[label="foo(3)",shape="box"]; 1099 n6[label="foo(2)",shape="box"]; 1100 n1->n2; 1101 n2->n3; 1102 n3->n4; 1103 n3->n5; 1104 n5->n6; 1105 `, 1106 flat: ` 1107 foo(1) 1108 goto L_1 1109 L_1: 1110 if false { 1111 goto L_2 1112 } else { 1113 goto L_3 1114 } 1115 L_2: 1116 return 4 1117 L_3: 1118 foo(3) 1119 goto L_4 1120 L_4: 1121 foo(2) 1122 goto L_1 1123 `, 1124 }, 1125 { 1126 name: "for full break", 1127 tree: []CStmt{ 1128 &CForStmt{ 1129 Init: numStmt(1), Cond: cIntLit(1, 10), Iter: numStmt(2), 1130 Body: *newBlock( 1131 &CIfStmt{ 1132 Cond: numCond(2), 1133 Then: newBlock( 1134 numStmt(3), 1135 &CBreakStmt{}, 1136 ), 1137 }, 1138 numStmt(4), 1139 ), 1140 }, 1141 ret(5), 1142 }, 1143 exp: ` 1144 n2[label="foo(1)",shape="box"]; 1145 n3[label="if false",shape="hexagon"]; 1146 n4[label="foo(2)",shape="box"]; 1147 n5[label="foo(4)",shape="box"]; 1148 n6[label="if 2",shape="hexagon"]; 1149 n7[label="foo(3)",shape="box"]; 1150 n8[label="return 5",shape="box"]; 1151 n1->n2; 1152 n2->n3; 1153 n3->n4[color="#99999955"]; 1154 n3->n2[color="#99999955"]; 1155 n3->n8[color="#00aa00"]; 1156 n3->n6[color="#aa0000"]; 1157 n4->n5[color="#99999955"]; 1158 n4->n3; 1159 n5->n6[color="#99999955"]; 1160 n5->n4; 1161 n6->n3[color="#99999955"]; 1162 n6->n7[color="#00aa00"]; 1163 n6->n5[color="#aa0000"]; 1164 n7->n6[color="#99999955"]; 1165 n7->n8; 1166 n8->n7[color="#99999955"]; 1167 n8->n3[color="#99999955"]; 1168 `, 1169 dom: ` 1170 n2[label="foo(1)",shape="box"]; 1171 n3[label="if false",shape="hexagon"]; 1172 n4[label="return 5",shape="box"]; 1173 n5[label="if 2",shape="hexagon"]; 1174 n6[label="foo(3)",shape="box"]; 1175 n7[label="foo(4)",shape="box"]; 1176 n8[label="foo(2)",shape="box"]; 1177 n1->n2; 1178 n2->n3; 1179 n3->n4; 1180 n3->n5; 1181 n5->n6; 1182 n5->n7; 1183 n7->n8; 1184 `, 1185 flat: ` 1186 foo(1) 1187 goto L_1 1188 L_1: 1189 if false { 1190 goto L_2 1191 } else { 1192 goto L_3 1193 } 1194 L_2: 1195 return 5 1196 L_3: 1197 if 2 { 1198 goto L_4 1199 } else { 1200 goto L_5 1201 } 1202 L_4: 1203 foo(3) 1204 goto L_2 1205 L_5: 1206 foo(4) 1207 goto L_6 1208 L_6: 1209 foo(2) 1210 goto L_1 1211 `, 1212 }, 1213 } 1214 1215 func writeDotFile(name string, data []byte) { 1216 name = strings.ReplaceAll(name, " ", "_") 1217 fname := name + ".dot" 1218 _ = ioutil.WriteFile(fname, data, 0644) 1219 sdata, _ := exec.Command("dot", "-Tsvg", fname).Output() 1220 _ = ioutil.WriteFile(name+".svg", sdata, 0644) 1221 _ = os.Remove(fname) 1222 } 1223 1224 func cleanDot(s string) string { 1225 s = strings.TrimPrefix(s, `digraph {`) 1226 s = strings.TrimSpace(s) 1227 s = strings.TrimPrefix(s, `n1[label="begin"];`) 1228 s = strings.TrimSuffix(s, `}`) 1229 s = strings.TrimSpace(s) 1230 return s 1231 } 1232 1233 func TestControlFlow(t *testing.T) { 1234 const dir = "testout" 1235 _ = os.MkdirAll(dir, 0755) 1236 for _, c := range casesControlFlow { 1237 t.Run(c.name, func(t *testing.T) { 1238 tr := newTranslator(libs.NewEnv(types.Config32()), Config{}) 1239 var fixer Visitor 1240 fixer = func(n Node) { 1241 switch n := n.(type) { 1242 case nil: 1243 return 1244 case *CVarDecl: 1245 n.g = tr 1246 case *CVarSpec: 1247 n.g = tr 1248 } 1249 n.Visit(fixer) 1250 } 1251 1252 for _, st := range c.tree { 1253 st.Visit(fixer) 1254 } 1255 1256 cf := tr.NewControlFlow(c.tree) 1257 1258 got := cf.dumpDot() 1259 writeDotFile(filepath.Join(dir, c.name), []byte(got)) 1260 got = cleanDot(got) 1261 require.Equal(t, strings.TrimSpace(c.exp), got) 1262 1263 got = cf.dumpDomDot() 1264 writeDotFile(filepath.Join(dir, c.name+"_dom"), []byte(got)) 1265 got = cleanDot(got) 1266 require.Equal(t, strings.TrimSpace(c.dom), got) 1267 1268 stmts := cf.Flatten() 1269 got = printStmts(stmts) 1270 got = strings.ReplaceAll(got, "\t", "") 1271 require.Equal(t, strings.TrimSpace(c.flat), got) 1272 }) 1273 } 1274 }