github.com/gotranspile/cxgo@v0.3.7/c_stmt.go (about) 1 package cxgo 2 3 import ( 4 "go/ast" 5 "go/token" 6 7 "github.com/gotranspile/cxgo/types" 8 ) 9 10 const optimizeStatements = false 11 12 type CStmt interface { 13 Node 14 AsStmt() []GoStmt 15 Uses() []types.Usage 16 } 17 18 type CStmtFunc func(s CStmt) ([]CStmt, bool) 19 20 type CCompStmt interface { 21 CStmt 22 EachStmt(fnc CStmtFunc) bool 23 } 24 25 type CStmtConv interface { 26 ToStmt() []CStmt 27 } 28 29 func cEachBlockStmt(fnc func([]CStmt), stmts []CStmt) { 30 fnc(stmts) 31 for _, s := range stmts { 32 switch s := s.(type) { 33 case *BlockStmt: 34 cEachBlockStmt(fnc, s.Stmts) 35 case *CIfStmt: 36 cEachBlockStmt(fnc, s.Then.Stmts) 37 if s.Else != nil { 38 cEachBlockStmt(fnc, []CStmt{s.Else}) 39 } 40 case *CForStmt: 41 cEachBlockStmt(fnc, s.Body.Stmts) 42 case *CSwitchStmt: 43 for _, c := range s.Cases { 44 cEachBlockStmt(fnc, c.Stmts) 45 } 46 } 47 } 48 } 49 50 func cEachStmt(fnc func(CStmt) bool, stmts []CStmt) { 51 for _, s := range stmts { 52 if !fnc(s) { 53 continue 54 } 55 c, ok := s.(CCompStmt) 56 if !ok { 57 continue 58 } 59 c.EachStmt(func(s CStmt) ([]CStmt, bool) { 60 cEachStmt(fnc, []CStmt{s}) 61 return nil, false 62 }) 63 } 64 } 65 66 func cReplaceEachStmt(fnc CStmtFunc, stmts []CStmt) ([]CStmt, bool) { 67 b := &BlockStmt{Stmts: stmts} 68 var replace CStmtFunc 69 replace = func(s CStmt) ([]CStmt, bool) { 70 if out, mod := fnc(s); mod { 71 return out, mod 72 } 73 if c, ok := s.(CCompStmt); ok { 74 c.EachStmt(replace) 75 } 76 return nil, false 77 } 78 if b.EachStmt(replace) { 79 return b.Stmts, true 80 } 81 return stmts, false 82 } 83 84 func cExprStmt(x Expr) []GoStmt { 85 if s, ok := x.(CStmt); ok { 86 return s.AsStmt() 87 } 88 switch x := x.(type) { 89 case CStmt: 90 return x.AsStmt() 91 case IntLit: 92 return nil 93 } 94 return []GoStmt{exprStmt(x.AsExpr())} 95 } 96 97 func cOneStmt(x CStmt) GoStmt { 98 arr := x.AsStmt() 99 if len(arr) != 1 { 100 panic("should only one element") 101 } 102 return arr[0] 103 } 104 105 var ( 106 _ CStmt = &CExprStmt{} 107 ) 108 109 func NewCExprStmt(e Expr) []CStmt { 110 e = cUnwrap(e) 111 if m, ok := e.(*CMultiExpr); ok { 112 out := make([]CStmt, 0, len(m.Exprs)) 113 for _, e := range m.Exprs { 114 out = append(out, NewCExprStmt(e)...) 115 } 116 return out 117 } 118 if s, ok := e.(CStmtConv); ok { 119 return s.ToStmt() 120 } 121 if ie, ok := e.(Ident); ok { 122 return []CStmt{&UnusedVar{Name: ie.Identifier()}} 123 } 124 return []CStmt{&CExprStmt{Expr: e}} 125 } 126 127 func NewCExprStmt1(e Expr) CStmt { 128 if e == nil { 129 return nil 130 } 131 e = cUnwrap(e) 132 if s, ok := e.(CStmtConv); ok { 133 arr := s.ToStmt() 134 if len(arr) == 1 { 135 return arr[0] 136 } 137 } 138 return &CExprStmt{Expr: e} 139 } 140 141 type CExprStmt struct { 142 Expr Expr 143 } 144 145 func (s *CExprStmt) Visit(v Visitor) { 146 v(s.Expr) 147 } 148 149 func (s *CExprStmt) CType() types.Type { 150 return s.Expr.CType(nil) 151 } 152 153 func (s *CExprStmt) AsExpr() GoExpr { 154 return s.Expr.AsExpr() 155 } 156 157 func (s *CExprStmt) AsStmt() []GoStmt { 158 return cExprStmt(s.Expr) 159 } 160 161 func (s *CExprStmt) Uses() []types.Usage { 162 return types.UseRead(s.Expr) 163 } 164 165 type CLabelStmt struct { 166 Label string 167 } 168 169 func (s *CLabelStmt) Visit(v Visitor) {} 170 171 func (s *CLabelStmt) AsStmt() []GoStmt { 172 return []GoStmt{&ast.LabeledStmt{ 173 Label: ident(s.Label), 174 Stmt: &ast.EmptyStmt{Implicit: true}, 175 }} 176 } 177 178 func (s *CLabelStmt) Uses() []types.Usage { 179 return nil 180 } 181 182 func (g *translator) NewCaseStmt(exp Expr, stmts ...CStmt) *CCaseStmt { 183 return &CCaseStmt{g: g, Expr: exp, Stmts: stmts} 184 } 185 186 type CCaseStmt struct { 187 g *translator 188 Expr Expr 189 Stmts []CStmt 190 } 191 192 func (s *CCaseStmt) Visit(v Visitor) { 193 v(s.Expr) 194 for _, st := range s.Stmts { 195 v(st) 196 } 197 } 198 199 func (s *CCaseStmt) GoCaseClause() *ast.CaseClause { 200 stmts := s.g.NewCBlock(s.Stmts...).GoBlockStmt() 201 if s.Expr == nil { 202 return &ast.CaseClause{ 203 Body: stmts.List, 204 } 205 } 206 return &ast.CaseClause{ 207 List: []GoExpr{s.Expr.AsExpr()}, 208 Body: stmts.List, 209 } 210 } 211 212 func (s *CCaseStmt) AsStmt() []GoStmt { 213 return []GoStmt{s.GoCaseClause()} 214 } 215 216 func (s *CCaseStmt) Uses() []types.Usage { 217 var list []types.Usage 218 if s.Expr != nil { 219 list = append(list, types.UseRead(s.Expr)...) 220 } 221 for _, c := range s.Stmts { 222 list = append(list, c.Uses()...) 223 } 224 return list 225 } 226 227 func (g *translator) NewCIfStmt(cond BoolExpr, then []CStmt, els IfElseStmt) *CIfStmt { 228 then = g.NewCBlock(then...).Stmts 229 //if els != nil { 230 // elss := g.NewCBlock(els).Stmts 231 // if len(elss) != 0 && len(then) == 1 { 232 // // TODO: check if both conditions has no side-effects 233 // _, ok := then[0].(*CIfStmt) 234 // _, ok2 := elss[0].(*CIfStmt) 235 // // "then" branch contains a single if statement 236 // // and "else" contains something else 237 // if ok && !ok2 { 238 // // invert if condition and swap branches 239 // return g.NewCIfStmt(g.cNot(cond), elss, then[0]) 240 // } 241 // } 242 // if len(elss) == 1 { 243 // if eif, ok := elss[0].(*CIfStmt); ok { 244 // els = eif 245 // } 246 // } 247 //} 248 return &CIfStmt{ 249 g: g, 250 Cond: cond, 251 Then: g.NewCBlock(then...), 252 Else: els, 253 } 254 } 255 256 var _ CCompStmt = &CIfStmt{} 257 258 type IfElseStmt interface { 259 CStmt 260 isElseStmt() 261 } 262 263 type CIfStmt struct { 264 g *translator 265 Cond BoolExpr 266 Then *BlockStmt 267 Else IfElseStmt 268 } 269 270 func (s *CIfStmt) isElseStmt() {} 271 272 func (s *CIfStmt) Visit(v Visitor) { 273 v(s.Cond) 274 v(s.Then) 275 v(s.Else) 276 } 277 278 func (g *translator) toElseStmt(st ...CStmt) IfElseStmt { 279 if len(st) == 0 { 280 return nil 281 } 282 if len(st) == 1 { 283 if e, ok := st[0].(IfElseStmt); ok { 284 return e 285 } 286 } 287 return g.newBlockStmt(st...) 288 } 289 290 func (s *CIfStmt) EachStmt(fnc CStmtFunc) bool { 291 mod := s.Then.EachStmt(fnc) 292 if s.Else == nil { 293 return mod 294 } 295 els := s.g.cBlockCast(s.Else) 296 mod2 := els.EachStmt(fnc) 297 if len(els.Stmts) == 0 { 298 s.Else = nil 299 } else if len(els.Stmts) == 1 { 300 s.Else = s.g.toElseStmt(els.Stmts[0]) 301 } else { 302 s.Else = els 303 } 304 return mod || mod2 305 } 306 307 func (s *CIfStmt) AsStmt() []GoStmt { 308 cond := s.Cond.AsExpr() 309 then := s.Then.GoBlockStmt().List 310 var els []GoStmt 311 if s.Else != nil { 312 els = s.Else.AsStmt() 313 } 314 return []GoStmt{ 315 ifelse(cond, then, els), 316 } 317 } 318 319 func (s *CIfStmt) Uses() []types.Usage { 320 var list []types.Usage 321 list = append(list, types.UseRead(s.Cond)...) 322 list = append(list, s.Then.Uses()...) 323 if s.Else != nil { 324 list = append(list, s.Else.Uses()...) 325 } 326 return list 327 } 328 329 func (g *translator) NewCSwitchStmt(cond Expr, stmts []CStmt) *CSwitchStmt { 330 sw := &CSwitchStmt{g: g, Cond: cond} 331 sw.addStmts(stmts) 332 // TODO: fix branches (break, fallthrough) 333 return sw 334 } 335 336 var _ CCompStmt = &CSwitchStmt{} 337 338 type CSwitchStmt struct { 339 g *translator 340 Cond Expr 341 Cases []*CCaseStmt 342 } 343 344 func (s *CSwitchStmt) Visit(v Visitor) { 345 v(s.Cond) 346 for _, c := range s.Cases { 347 v(c) 348 } 349 } 350 351 func (s *CSwitchStmt) addStmts(stmts []CStmt) { 352 for _, st := range s.g.NewCBlock(stmts...).Stmts { 353 if c, ok := st.(*CCaseStmt); ok { 354 sub := c.Stmts 355 c.Stmts = nil 356 s.Cases = append(s.Cases, c) 357 if len(sub) != 0 { 358 s.addStmts(sub) 359 } 360 } else { 361 last := s.Cases[len(s.Cases)-1] 362 last.Stmts = append(last.Stmts, st) 363 } 364 } 365 } 366 367 func (s *CSwitchStmt) EachStmt(fnc CStmtFunc) bool { 368 gmod := false 369 for _, c := range s.Cases { 370 var mod bool 371 c.Stmts, mod = eachStmtIn(c.Stmts, fnc) 372 gmod = gmod || mod 373 } 374 return gmod 375 } 376 377 func (s *CSwitchStmt) AsStmt() []GoStmt { 378 var stmts []GoStmt 379 for i, c := range s.Cases { 380 cs := c.GoCaseClause() 381 sub := cs.Body 382 if len(sub) == 0 { 383 if i != len(s.Cases)-1 { 384 sub = []GoStmt{&ast.BranchStmt{Tok: token.FALLTHROUGH}} 385 } 386 } else { 387 switch b := sub[len(sub)-1].(type) { 388 case *ast.BranchStmt: 389 if b.Tok == token.BREAK { 390 sub = sub[:len(sub)-1] 391 } 392 case *ast.ReturnStmt: 393 default: 394 if i != len(s.Cases)-1 { 395 sub = append(sub, &ast.BranchStmt{Tok: token.FALLTHROUGH}) 396 } 397 } 398 } 399 cs.Body = sub 400 stmts = append(stmts, cs) 401 } 402 return []GoStmt{&ast.SwitchStmt{ 403 Tag: s.Cond.AsExpr(), 404 Body: block(stmts...), 405 }} 406 } 407 408 func (s *CSwitchStmt) Uses() []types.Usage { 409 var list []types.Usage 410 list = append(list, types.UseRead(s.Cond)...) 411 for _, c := range s.Cases { 412 list = append(list, c.Uses()...) 413 } 414 return list 415 } 416 417 func (g *translator) NewCForDeclStmt(init CDecl, cond BoolExpr, iter Expr, stmts []CStmt) *CForStmt { 418 if cIsTrue(cond) { 419 cond = nil 420 } 421 f := &CForStmt{ 422 Init: g.NewCDeclStmt1(init), 423 Cond: cond, 424 Iter: NewCExprStmt1(iter), 425 } 426 if body := g.NewCBlock(stmts...); body != nil { 427 f.Body = *body 428 } 429 return f 430 } 431 432 func (g *translator) NewCForStmt(init Expr, cond BoolExpr, iter Expr, stmts []CStmt) *CForStmt { 433 if cIsTrue(cond) { 434 cond = nil 435 } 436 f := &CForStmt{ 437 Init: NewCExprStmt1(init), 438 Cond: cond, 439 Iter: NewCExprStmt1(iter), 440 } 441 if body := g.NewCBlock(stmts...); body != nil { 442 f.Body = *body 443 } 444 return f 445 } 446 447 var _ CCompStmt = &CForStmt{} 448 449 type CForStmt struct { 450 Init CStmt 451 Cond Expr 452 Iter CStmt 453 Body BlockStmt 454 } 455 456 func (s *CForStmt) Visit(v Visitor) { 457 v(s.Init) 458 v(s.Cond) 459 v(s.Iter) 460 v(&s.Body) 461 } 462 463 func (s *CForStmt) EachStmt(fnc CStmtFunc) bool { 464 return s.Body.EachStmt(fnc) 465 } 466 467 func (s *CForStmt) AsStmt() []GoStmt { 468 var init GoStmt 469 if s.Init != nil { 470 // if we have only one init decl with one spec -> convert to inline for decl 471 init = cOneStmt(s.Init) 472 if s, ok := init.(*ast.DeclStmt); ok { 473 if g, ok := s.Decl.(*ast.GenDecl); ok && g.Tok == token.VAR && len(g.Specs) == 1 { 474 if sp, ok := g.Specs[0].(*ast.ValueSpec); ok && len(sp.Names) == len(sp.Values) { 475 var ( 476 lhs []ast.Expr 477 rhs []ast.Expr 478 ) 479 for i := range sp.Names { 480 lhs = append(lhs, sp.Names[i]) 481 rhs = append(rhs, &ast.CallExpr{Fun: sp.Type, Args: []ast.Expr{sp.Values[i]}}) 482 } 483 init = &ast.AssignStmt{ 484 Lhs: lhs, 485 Tok: token.DEFINE, 486 Rhs: rhs, 487 } 488 } 489 } 490 } 491 } 492 var iter GoStmt 493 if s.Iter != nil { 494 iter = cOneStmt(s.Iter) 495 } 496 var cond GoExpr 497 if s.Cond != nil { 498 cond = s.Cond.AsExpr() 499 } 500 return []GoStmt{&ast.ForStmt{ 501 Init: init, 502 Cond: cond, 503 Post: iter, 504 Body: s.Body.GoBlockStmt(), 505 }} 506 } 507 508 func (s *CForStmt) Uses() []types.Usage { 509 var list []types.Usage 510 if s.Init != nil { 511 list = append(list, s.Init.Uses()...) 512 } 513 if s.Cond != nil { 514 list = append(list, types.UseRead(s.Cond)...) 515 } 516 if s.Iter != nil { 517 list = append(list, s.Iter.Uses()...) 518 } 519 list = append(list, s.Body.Uses()...) 520 return list 521 } 522 523 // forCanBreak returns true in case a for statement can be exited at some point to continue execution of the function. 524 // This means that it will return false for loops that return from the function directly. 525 func (g *translator) forCanBreak(s *CForStmt) bool { 526 if s.Cond != nil { 527 v, ok := cIsBoolConst(s.Cond) 528 if !ok { 529 // not a constant, so should break at some point... most probably 530 return true 531 } 532 if !v { 533 // breaks instantly 534 return true 535 } 536 } 537 // "infinite", but can still break manually, so find break statements 538 return g.hasBreaks(&s.Body) 539 } 540 541 // switchCanBreak returns true in case a switch statement can be exited at some point to continue execution of the function. 542 // This means that it will return false for switches that return from all the branches. 543 func (g *translator) switchCanBreak(s *CSwitchStmt) bool { 544 if len(s.Cases) == 0 { 545 return true 546 } 547 for i, c := range s.Cases { 548 if len(c.Stmts) == 0 { 549 if i == len(c.Stmts)-1 { 550 // last is empty - will exit 551 return true 552 } 553 // fallthrough - ignore 554 continue 555 } 556 if !g.allBranchesJump(c.Stmts[len(c.Stmts)-1]) { 557 return true 558 } 559 } 560 return false 561 } 562 563 func (g *translator) NewCDoWhileStmt(cond Expr, stmts []CStmt) CStmt { 564 stmts = append(stmts, g.NewCIfStmt( 565 g.cNot(cond), []CStmt{&CBreakStmt{}}, nil, 566 )) 567 return g.NewCForStmt(nil, nil, nil, stmts) 568 } 569 570 type CGotoStmt struct { 571 Label string 572 } 573 574 func (s *CGotoStmt) Visit(v Visitor) {} 575 576 func (s *CGotoStmt) AsStmt() []GoStmt { 577 return []GoStmt{&ast.BranchStmt{Tok: token.GOTO, Label: ident(s.Label)}} 578 } 579 580 func (s *CGotoStmt) Uses() []types.Usage { 581 return nil 582 } 583 584 func countContinue(stmts ...CStmt) int { 585 var cnt int 586 cEachStmt(func(s CStmt) bool { 587 switch s.(type) { 588 case *CContinueStmt: 589 cnt++ 590 case *CForStmt: 591 return false 592 } 593 return true 594 }, stmts) 595 return cnt 596 } 597 598 func countBreak(stmts ...CStmt) int { 599 var cnt int 600 cEachStmt(func(s CStmt) bool { 601 switch s.(type) { 602 case *CBreakStmt: 603 cnt++ 604 case *CForStmt: 605 return false 606 } 607 return true 608 }, stmts) 609 return cnt 610 } 611 612 type CContinueStmt struct{} 613 614 func (s *CContinueStmt) Visit(v Visitor) {} 615 616 func (s *CContinueStmt) AsStmt() []GoStmt { 617 return []GoStmt{&ast.BranchStmt{Tok: token.CONTINUE}} 618 } 619 620 func (s *CContinueStmt) Uses() []types.Usage { 621 return nil 622 } 623 624 type CBreakStmt struct{} 625 626 func (s *CBreakStmt) Visit(v Visitor) {} 627 628 func (s *CBreakStmt) AsStmt() []GoStmt { 629 return []GoStmt{&ast.BranchStmt{Tok: token.BREAK}} 630 } 631 632 func (s *CBreakStmt) Uses() []types.Usage { 633 return nil 634 } 635 636 func (g *translator) NewReturnStmt(x Expr, rtyp types.Type) []CStmt { 637 x = cUnwrap(x) 638 switch x := x.(type) { 639 case *CTernaryExpr: 640 // return (v ? a : b) -> if v { return a } return b 641 var stmts []CStmt 642 stmts = append(stmts, g.NewCIfStmt(x.Cond, g.NewReturnStmt(x.Then, rtyp), nil)) 643 stmts = append(stmts, g.NewReturnStmt(x.Else, rtyp)...) 644 return stmts 645 } 646 if rtyp != nil { 647 x = g.cCast(rtyp, x) 648 } 649 return []CStmt{&CReturnStmt{ 650 Expr: x, 651 }} 652 } 653 654 func (g *translator) NewZeroReturnStmt(rtyp types.Type) []CStmt { 655 return g.NewReturnStmt(g.ZeroValue(rtyp), rtyp) 656 } 657 658 type CReturnStmt struct { 659 Expr Expr 660 } 661 662 func (s *CReturnStmt) Visit(v Visitor) { 663 v(s.Expr) 664 } 665 666 func (s *CReturnStmt) AsStmt() []GoStmt { 667 if s.Expr == nil { 668 return []GoStmt{returnStmt()} 669 } 670 return []GoStmt{returnStmt(s.Expr.AsExpr())} 671 } 672 673 func (s *CReturnStmt) Uses() []types.Usage { 674 var list []types.Usage 675 if s.Expr != nil { 676 list = append(list, types.UseRead(s.Expr)...) 677 } 678 return list 679 } 680 681 func (g *translator) setReturnType(ret types.Type, stmts []CStmt) { 682 if ret == nil { 683 return 684 } 685 for _, s := range stmts { 686 switch s := s.(type) { 687 case *CReturnStmt: 688 s.Expr = g.cCast(ret, s.Expr) 689 case *BlockStmt: 690 g.setReturnType(ret, s.Stmts) 691 case *CIfStmt: 692 g.setReturnType(ret, s.Then.Stmts) 693 if s.Else != nil { 694 g.setReturnType(ret, []CStmt{s.Else}) 695 } 696 case *CForStmt: 697 g.setReturnType(ret, s.Body.Stmts) 698 case *CSwitchStmt: 699 for _, c := range s.Cases { 700 g.setReturnType(ret, c.Stmts) 701 } 702 } 703 } 704 } 705 706 func (g *translator) cBlockCast(s CStmt) *BlockStmt { 707 if b, ok := s.(*BlockStmt); ok { 708 return b 709 } 710 return g.newBlockStmt(s) 711 } 712 713 func stmtHasDecl(stmts ...CStmt) bool { 714 for _, st := range stmts { 715 switch st.(type) { 716 case *CDeclStmt: 717 return true 718 } 719 } 720 return false 721 } 722 723 func flattenBlocks(stmts []CStmt) ([]CStmt, bool) { 724 mod := false 725 for i := 0; i < len(stmts); i++ { 726 s, ok := stmts[i].(*BlockStmt) 727 if !ok || stmtHasDecl(s.Stmts...) { 728 continue 729 } 730 arr := append([]CStmt{}, stmts[:i]...) 731 arr = append(arr, s.Stmts...) 732 arr = append(arr, stmts[i+1:]...) 733 stmts = arr 734 mod = true 735 i += len(s.Stmts) - 1 736 } 737 return stmts, mod 738 } 739 740 func (g *translator) isReturnOrExit(s CStmt) bool { 741 switch s := s.(type) { 742 case *CReturnStmt: 743 return true 744 case *CExprStmt: 745 switch e := s.Expr.(type) { 746 case *CallExpr: 747 if id, ok := e.Fun.(Ident); ok { 748 switch id.Identifier() { 749 case g.env.Go().OsExitFunc(), 750 g.env.Go().PanicFunc(): 751 return true 752 } 753 } 754 } 755 } 756 return false 757 } 758 759 func isEmptyReturn(s CStmt) bool { 760 r, ok := s.(*CReturnStmt) 761 return ok && r.Expr == nil 762 } 763 764 func (g *translator) isHardJump(s CStmt) bool { 765 switch s.(type) { 766 case *CReturnStmt, *CGotoStmt: 767 return true 768 } 769 return false 770 } 771 772 func (g *translator) isJump(s CStmt) bool { 773 if g.isHardJump(s) { 774 return true 775 } 776 switch s.(type) { 777 case *CContinueStmt, *CBreakStmt: 778 return true 779 } 780 return false 781 } 782 783 func (g *translator) allBranchesJump(s CStmt) bool { 784 if g.isJump(s) { 785 return true 786 } 787 switch s := s.(type) { 788 case *BlockStmt: 789 if len(s.Stmts) == 0 { 790 return false 791 } 792 return g.allBranchesJump(s.Stmts[len(s.Stmts)-1]) 793 case *CIfStmt: 794 if !g.allBranchesJump(s.Then) { 795 return false 796 } 797 if s.Else == nil { 798 return false // yes, in other cases we'll miss false condition jump 799 } 800 return g.allBranchesJump(s.Else) 801 case *CSwitchStmt: 802 return !g.switchCanBreak(s) 803 case *CForStmt: 804 return !g.forCanBreak(s) 805 } 806 return false 807 } 808 809 func dupStmts(stmts []CStmt) []CStmt { 810 out, _ := cReplaceEachStmt(func(s CStmt) ([]CStmt, bool) { 811 _, ok := s.(*CLabelStmt) 812 return nil, ok 813 }, stmts) 814 return out 815 } 816 817 // invertLastIf finds an if statements that can be inverted. 818 // It checks in an statement body is smaller than all the other statements 819 // that follow this if. 820 // This function can only work in function bodies, because it relies on the returns. 821 func (g *translator) invertLastIf(stmts []CStmt) ([]CStmt, bool) { 822 n := len(stmts) 823 if n < 2 { 824 return stmts, false 825 } 826 var ( 827 ind = -1 828 iff *CIfStmt 829 isRet = false 830 ) 831 for i := n - 1; i > 0; i-- { 832 f, ok := stmts[i].(*CIfStmt) 833 if !ok || f.Else != nil || len(f.Then.Stmts) == 0 { 834 continue 835 } 836 fn := len(f.Then.Stmts) 837 isRet = g.isHardJump(f.Then.Stmts[fn-1]) 838 if isRet && fn == 1 { 839 continue 840 } 841 if cost := stmtCost(stmts[i+1:]...); !isRet && cost > 2 { 842 continue 843 } else if cost >= stmtCost(f.Then.Stmts...) { 844 continue 845 } 846 ind, iff = i, f 847 break 848 } 849 if iff == nil { 850 return stmts, false 851 } 852 ret := append([]CStmt{}, stmts[ind+1:]...) 853 if len(ret) == 0 || !g.isHardJump(ret[len(ret)-1]) { 854 ret = append(ret, &CReturnStmt{}) 855 } 856 iff.Cond = g.cNot(iff.Cond) 857 b := iff.Then 858 iff.Then = g.NewCBlock(append([]CStmt{}, stmts[ind+1:]...)...) 859 if n := len(iff.Then.Stmts); n == 0 || !g.isHardJump(iff.Then.Stmts[n-1]) { 860 if l, ok := ret[0].(*CLabelStmt); ok { 861 iff.Then.Stmts = append(iff.Then.Stmts, &CGotoStmt{Label: l.Label}) 862 } else { 863 iff.Then.Stmts = append(iff.Then.Stmts, dupStmts(ret)...) 864 } 865 } 866 stmts = append(stmts[:ind+1], b.Stmts...) 867 if !g.isHardJump(stmts[len(stmts)-1]) { 868 stmts = append(stmts, ret...) 869 } 870 if isEmptyReturn(stmts[len(stmts)-1]) { 871 stmts = stmts[:len(stmts)-1] 872 } 873 return stmts, true 874 } 875 876 // moveLastReturnToLastIf finds an if statement with else branch, that is before the return and 877 // moves the return to all the if branches. 878 // It helps apply other simplifications later. 879 func (g *translator) moveLastReturnToLastIf(stmts []CStmt) ([]CStmt, bool) { 880 n := len(stmts) 881 if n < 2 { 882 return stmts, false 883 } 884 ret, ok := stmts[n-1].(*CReturnStmt) 885 if !ok { 886 return stmts, false 887 } 888 iff, ok := stmts[n-2].(*CIfStmt) 889 if !ok || iff.Else == nil { 890 return stmts, false 891 } 892 stmts = stmts[:n-1] 893 n-- 894 allReturns := true 895 mod := false 896 for iff != nil { 897 if !g.isJump(iff.Then.Stmts[len(iff.Then.Stmts)-1]) { 898 iff.Then.Stmts = append(iff.Then.Stmts, ret) 899 mod = true 900 } 901 if iff.Else == nil { 902 allReturns = false 903 break 904 } 905 els := iff.Else 906 switch els := els.(type) { 907 case *BlockStmt: 908 if !g.isJump(els.Stmts[len(els.Stmts)-1]) { 909 els.Stmts = append(els.Stmts, ret) 910 mod = true 911 } 912 iff = nil 913 case *CIfStmt: 914 iff = els 915 default: 916 if !g.isJump(els) { 917 iff.Else = g.NewCBlock(els, ret) 918 mod = true 919 } 920 iff = nil 921 } 922 } 923 if !allReturns { 924 stmts = append(stmts, ret) 925 } 926 return stmts, mod 927 } 928 929 // inlineElseInLastReturn finds a return in the end and inlines its else branch 930 // if all the branches contain jumps at the end. 931 func (g *translator) inlineElseInLastReturn(stmts []CStmt) ([]CStmt, bool) { 932 n := len(stmts) 933 if n < 1 { 934 return stmts, false 935 } 936 iff, ok := stmts[n-1].(*CIfStmt) 937 if !ok || iff.Else == nil { 938 return stmts, false 939 } 940 if !g.allBranchesJump(iff.Then) || !g.allBranchesJump(iff.Else) { 941 return stmts, false 942 } 943 els := iff.Else 944 iff.Else = nil 945 switch els := els.(type) { 946 case *BlockStmt: 947 stmts = append(stmts, els.Stmts...) 948 default: 949 stmts = append(stmts, els) 950 } 951 return stmts, true 952 } 953 954 func (g *translator) inlineSmallGotos(stmts []CStmt) ([]CStmt, bool) { 955 const maxCost = 5 956 // first, collect all paths from the label 957 // we only care about direct paths that lead to a "hard jump" (return or another goto) 958 labels := make(map[string][]CStmt) 959 seen := make(map[*CLabelStmt]struct{}) 960 cEachBlockStmt(func(stmts []CStmt) { 961 for i, s := range stmts { 962 l, ok := s.(*CLabelStmt) 963 if !ok { 964 continue 965 } else if _, ok := seen[l]; ok { 966 continue 967 } 968 seen[l] = struct{}{} 969 cnt := 0 970 for j, s := range stmts[i:] { 971 if g.isHardJump(s) { 972 cnt = j + 1 973 break 974 } 975 } 976 if cnt == 0 { 977 continue 978 } 979 body := append([]CStmt{}, stmts[i+1:i+cnt]...) 980 if stmtCost(body...) > maxCost { 981 continue 982 } 983 labels[l.Label] = body 984 } 985 }, stmts) 986 seen = nil 987 // rescan label bodies and replace gotos in them as well 988 for name, body := range labels { 989 mod, del := false, false 990 body, _ = cReplaceEachStmt(func(s CStmt) ([]CStmt, bool) { 991 if del { 992 return nil, false 993 } 994 g, ok := s.(*CGotoStmt) 995 if !ok { 996 return nil, false 997 } 998 if name == g.Label { 999 // loop - delete the label 1000 delete(labels, name) 1001 del = true 1002 return nil, false 1003 } 1004 if body := labels[g.Label]; len(body) != 0 { 1005 mod = true 1006 return body, true 1007 } 1008 return nil, false 1009 }, body) 1010 if mod && !del { 1011 labels[name] = body 1012 } 1013 } 1014 if len(labels) == 0 { 1015 return stmts, false 1016 } 1017 // replace gotos with those paths and remove labels 1018 stmts, _ = cReplaceEachStmt(func(s CStmt) ([]CStmt, bool) { 1019 if l, ok := s.(*CLabelStmt); ok { 1020 if len(labels[l.Label]) != 0 { 1021 return nil, true 1022 } 1023 } 1024 g, ok := s.(*CGotoStmt) 1025 if !ok { 1026 return nil, false 1027 } 1028 if body := labels[g.Label]; len(body) != 0 { 1029 return body, true 1030 } 1031 return nil, false 1032 }, stmts) 1033 return stmts, true 1034 } 1035 1036 func (g *translator) NewCBlock(stmts ...CStmt) *BlockStmt { 1037 if len(stmts) == 1 { 1038 if b, ok := stmts[0].(*BlockStmt); ok { 1039 return b 1040 } 1041 } 1042 // TODO: 1043 //if len(stmts) >= 2 { 1044 // l := len(stmts) 1045 // ret, ok1 := stmts[l-1].(*ast.ReturnStmt) 1046 // ifs, ok2 := stmts[l-2].(*ast.IfStmt) 1047 // if ok1 && ok2 && ifs.Else == nil && len(ifs.Body.List) > 1 { 1048 // ifs.Cond = not(ifs.Cond) 1049 // body := ifs.Body.List 1050 // ifs.Body.List = []GoStmt{ret} 1051 // if _, ok := body[len(body)-1].(*ast.ReturnStmt); !ok { 1052 // body = append(body, ret) 1053 // } 1054 // stmts = append(stmts[:l-1], body...) 1055 // return block(stmts...) 1056 // } 1057 //} 1058 for { 1059 var mod bool 1060 stmts, mod = flattenBlocks(stmts) 1061 if mod { 1062 continue 1063 } 1064 if optimizeStatements { 1065 stmts, mod = replaceBreaks(stmts) 1066 if mod { 1067 continue 1068 } 1069 stmts, mod = rebuildFors(stmts) 1070 if mod { 1071 continue 1072 } 1073 } 1074 break 1075 } 1076 return g.newBlockStmt(stmts...) 1077 } 1078 1079 func isIfReturn(s CStmt) (Expr, *CReturnStmt, bool) { 1080 iff, ok := s.(*CIfStmt) 1081 if !ok || iff.Else != nil || len(iff.Then.Stmts) != 1 { 1082 return nil, nil, false 1083 } 1084 ret, ok := iff.Then.Stmts[0].(*CReturnStmt) 1085 if !ok { 1086 return nil, nil, false 1087 } 1088 return iff.Cond, ret, true 1089 } 1090 1091 //func tryMergeForPreCond(cond Expr, ret *CReturnStmt, st CStmt) bool { 1092 // forr, ok := st.(*CForStmt) 1093 // if !ok || forr.Cond != nil || forr.Iter != nil || forr.Body == nil { 1094 // return false 1095 // } 1096 // n := len(forr.Body.Stmts) 1097 // if n == 0 { 1098 // return false 1099 // } 1100 // cond2, ret2, ok := isIfReturn(forr.Body.Stmts[n-1]) 1101 // if !ok { 1102 // return false 1103 // } 1104 // if countContinue(forr.Body.Stmts...) != 0 { 1105 // return false 1106 // } 1107 // if 1108 //} 1109 1110 func revFindInfiniteFor(stmts []CStmt) int { 1111 for i := len(stmts) - 1; i >= 0; i-- { 1112 if f, ok := stmts[i].(*CForStmt); ok && f.Cond == nil { 1113 return i 1114 } 1115 } 1116 return -1 1117 } 1118 1119 func replaceBreaks(stmts []CStmt) ([]CStmt, bool) { 1120 n := len(stmts) 1121 if n < 2 { 1122 return stmts, false 1123 } 1124 _, ok := stmts[n-1].(*CReturnStmt) 1125 if !ok { 1126 return stmts, false 1127 } 1128 for i := n - 1; i >= 0 && n-i < 5; i = revFindInfiniteFor(stmts[:i]) { 1129 forr, ok := stmts[i].(*CForStmt) 1130 if !ok { 1131 continue 1132 } 1133 part := stmts[i+1:] 1134 b := countBreak(&forr.Body) 1135 if b != 1 { 1136 continue 1137 } 1138 r := 0 1139 var replace CStmtFunc 1140 replace = func(s CStmt) ([]CStmt, bool) { 1141 switch s := s.(type) { 1142 case *CBreakStmt: 1143 r++ 1144 return part, true 1145 case *CForStmt: 1146 return nil, false 1147 case CCompStmt: 1148 s.EachStmt(replace) 1149 } 1150 return nil, false 1151 } 1152 forr.EachStmt(replace) 1153 if r != b { 1154 panic(r) 1155 } 1156 return stmts[:i+1], true 1157 } 1158 return stmts, false 1159 } 1160 1161 func rebuildFors(stmts []CStmt) ([]CStmt, bool) { 1162 n := len(stmts) 1163 if n < 2 { 1164 return stmts, false 1165 } 1166 //for i, s := range stmts[:n-1] { 1167 // j := i+1 1168 // s2 := stmts[j] 1169 // if cond, ret, ok := isIfReturn(s); ok && tryMergeForPreCond(cond, ret, s2) { 1170 // stmts = append(stmts[:i], stmts[i+1:]...) 1171 // continue rules 1172 // } 1173 //} 1174 return stmts, false 1175 // TODO: 1176 //for i, s := range stmts[:len(stmts)-1] { 1177 // as, ok := s.(*ast.AssignStmt) 1178 // if !ok || len(as.Lhs) != 1 || as.Tok != token.ASSIGN { 1179 // continue 1180 // } 1181 // x, ok := as.Lhs[0].(*ast.Ident) 1182 // if !ok { 1183 // continue 1184 // } 1185 // if lit, ok := as.Rhs[0].(*ast.BasicLit); !ok || lit.Kind != token.INT { 1186 // continue 1187 // } 1188 // loop, ok := stmts[i+1].(*ast.ForStmt) 1189 // if !ok || loop.Init != nil || loop.Cond != nil || loop.Post != nil || loop.Body == nil || len(loop.Body.List) < 2 { 1190 // continue 1191 // } 1192 // body := loop.Body.List 1193 // l := len(body) 1194 // inc, ok := body[l-2].(*ast.IncDecStmt) 1195 // if !ok { 1196 // continue 1197 // } else if x2, ok := inc.X.(*ast.Ident); !ok || x2.Name != x.Name { 1198 // continue 1199 // } 1200 // ifc, ok := body[l-1].(*ast.IfStmt) 1201 // if !ok || ifc.Init != nil || ifc.Else != nil || len(ifc.Body.List) != 1 { 1202 // continue 1203 // } else if b, ok := ifc.Body.List[0].(*ast.BranchStmt); !ok || b.Tok != token.BREAK || b.Label != nil { 1204 // continue 1205 // } 1206 // loop.Init = as 1207 // loop.Cond = not(ifc.Cond) 1208 // loop.Post = inc 1209 // loop.Body.List = body[:l-2] 1210 // return append(stmts[:i], rebuildFors(stmts[i+1:])...) 1211 //} 1212 } 1213 1214 var _ CCompStmt = &BlockStmt{} 1215 1216 func (g *translator) newBlockStmt(stmts ...CStmt) *BlockStmt { 1217 return &BlockStmt{ 1218 g: g, 1219 Stmts: stmts, 1220 } 1221 } 1222 1223 type BlockStmt struct { 1224 g *translator 1225 Stmts []CStmt 1226 } 1227 1228 func (s *BlockStmt) isElseStmt() {} 1229 1230 func (s *BlockStmt) Visit(v Visitor) { 1231 for _, st := range s.Stmts { 1232 v(st) 1233 } 1234 } 1235 1236 func eachStmtIn(stmts []CStmt, fnc CStmtFunc) ([]CStmt, bool) { 1237 gmod := false 1238 for i := 0; i < len(stmts); i++ { 1239 arr, mod := fnc(stmts[i]) 1240 if !mod { 1241 continue 1242 } 1243 if !gmod { 1244 stmts = append([]CStmt{}, stmts...) 1245 } 1246 gmod = true 1247 if d := len(arr); d == 0 { 1248 stmts = append(stmts[:i], stmts[i+1:]...) 1249 i-- 1250 } else if d == 1 { 1251 stmts[i] = arr[0] 1252 } else if n := len(stmts); i == n-1 { 1253 stmts = append(stmts[:i], arr...) 1254 break 1255 } else { 1256 tmp := append([]CStmt{}, stmts[:i]...) 1257 tmp = append(tmp, arr...) 1258 tmp = append(tmp, stmts[i+1:]...) 1259 stmts = tmp 1260 i += len(arr) - 1 1261 } 1262 } 1263 return stmts, gmod 1264 } 1265 1266 func (s *BlockStmt) EachStmt(fnc CStmtFunc) bool { 1267 var mod bool 1268 s.Stmts, mod = eachStmtIn(s.Stmts, fnc) 1269 return mod 1270 } 1271 1272 func (s *BlockStmt) In(ft *types.FuncType) *BlockStmt { 1273 if ft.Return() != nil { 1274 s.g.setReturnType(ft.Return(), s.Stmts) 1275 } 1276 stmts := s.Stmts 1277 for optimizeStatements { 1278 var mod bool 1279 stmts, mod = s.g.moveLastReturnToLastIf(stmts) 1280 if mod { 1281 continue 1282 } 1283 stmts, mod = s.g.inlineElseInLastReturn(stmts) 1284 if mod { 1285 continue 1286 } 1287 stmts, mod = s.g.invertLastIf(stmts) 1288 if mod { 1289 continue 1290 } 1291 stmts, mod = s.g.inlineSmallGotos(stmts) 1292 if mod { 1293 continue 1294 } 1295 break 1296 } 1297 s.Stmts = stmts 1298 return s 1299 } 1300 1301 func asStmts(arr []CStmt) []GoStmt { 1302 var out []GoStmt 1303 for _, s := range arr { 1304 out = append(out, s.AsStmt()...) 1305 } 1306 return out 1307 } 1308 1309 func (s *BlockStmt) GoBlockStmt() *ast.BlockStmt { 1310 if s == nil { 1311 return nil 1312 } 1313 return block(asStmts(s.Stmts)...) 1314 } 1315 1316 func (s *BlockStmt) AsStmt() []GoStmt { 1317 return []GoStmt{s.GoBlockStmt()} 1318 } 1319 1320 func (s *BlockStmt) Uses() []types.Usage { 1321 var list []types.Usage 1322 for _, s := range s.Stmts { 1323 list = append(list, s.Uses()...) 1324 } 1325 return list 1326 } 1327 1328 func (g *translator) NewCDeclStmt1(decl CDecl) CStmt { 1329 return &CDeclStmt{Decl: decl} 1330 } 1331 1332 func (g *translator) NewCDeclStmt(decl CDecl) []CStmt { 1333 switch d := decl.(type) { 1334 case *CVarDecl: 1335 for i, v := range d.Inits { 1336 switch v.(type) { 1337 case *CTernaryExpr: 1338 var stmts []CStmt 1339 if pre := d.Inits[:i:i]; len(pre) > 0 { 1340 dc := *d 1341 dc.Inits = pre 1342 dc.Names = dc.Names[:i:i] 1343 stmts = append(stmts, g.NewCDeclStmt(&dc)...) 1344 } 1345 dt := *d 1346 dt.Inits = nil 1347 dt.Names = []*types.Ident{d.Names[i]} 1348 stmts = append(stmts, g.NewCDeclStmt(&dt)...) 1349 stmts = append(stmts, g.NewCAssignStmt(IdentExpr{d.Names[i]}, "", v)...) 1350 if post := d.Inits[i+1:]; len(post) > 0 { 1351 dc := *d 1352 dc.Inits = post 1353 dc.Names = dc.Names[i+1:] 1354 stmts = append(stmts, g.NewCDeclStmt(&dc)...) 1355 } 1356 return stmts 1357 } 1358 } 1359 } 1360 return []CStmt{&CDeclStmt{Decl: decl}} 1361 } 1362 1363 type CDeclStmt struct { 1364 Decl CDecl 1365 } 1366 1367 func (s *CDeclStmt) Visit(v Visitor) { 1368 v(s.Decl) 1369 } 1370 1371 func (s *CDeclStmt) AsStmt() []GoStmt { 1372 var out []GoStmt 1373 for _, d := range s.Decl.AsDecl() { 1374 out = append(out, &ast.DeclStmt{Decl: d}) 1375 } 1376 return out 1377 } 1378 1379 func (s *CDeclStmt) Uses() []types.Usage { 1380 return s.Decl.Uses() 1381 } 1382 1383 func (g *translator) NewCIncStmt(x Expr, decr bool) *CIncrStmt { 1384 return &CIncrStmt{ 1385 g: g, 1386 Expr: x, 1387 Decr: decr, 1388 } 1389 } 1390 1391 type CIncrStmt struct { 1392 g *translator 1393 Expr Expr 1394 Decr bool 1395 } 1396 1397 func (s *CIncrStmt) Visit(v Visitor) { 1398 v(s.Expr) 1399 } 1400 1401 func (s *CIncrStmt) AsStmt() []GoStmt { 1402 if s.Expr.CType(nil).Kind().IsPtr() { 1403 var arg Expr 1404 if s.Decr { 1405 arg = cIntLit(-1) 1406 } else { 1407 arg = cIntLit(+1) 1408 } 1409 x := cPtrOffset(s.g.ToPointer(s.Expr), arg) 1410 return asStmts(s.g.NewCAssignStmt(s.Expr, "", x)) 1411 } 1412 var tok token.Token 1413 if s.Decr { 1414 tok = token.DEC 1415 } else { 1416 tok = token.INC 1417 } 1418 return []GoStmt{&ast.IncDecStmt{ 1419 X: s.Expr.AsExpr(), 1420 Tok: tok, 1421 }} 1422 } 1423 1424 func (s *CIncrStmt) Uses() []types.Usage { 1425 var list []types.Usage 1426 list = append(list, types.UseRead(s.Expr)...) 1427 list = append(list, types.UseWrite(s.Expr)...) 1428 return list 1429 } 1430 1431 func (g *translator) NewCAssignStmtP(x Expr, op BinaryOp, y Expr) *CAssignStmt { 1432 x = cUnwrap(x) 1433 y = cUnwrap(y) 1434 r := g.cCast(x.CType(nil), y) 1435 return &CAssignStmt{ 1436 g: g, 1437 Left: x, 1438 Op: op, 1439 Right: r, 1440 } 1441 } 1442 1443 func (g *translator) NewCAssignStmt(x Expr, op BinaryOp, y Expr) []CStmt { 1444 x = cUnwrap(x) 1445 y = cUnwrap(y) 1446 if !x.HasSideEffects() { 1447 switch y := unwrapCasts(y).(type) { 1448 case *CUnaryExpr: 1449 switch z := unwrapCasts(y.Expr).(type) { 1450 case *CTernaryExpr: 1451 // v = -(x ? a : b) -> if x { v = -a } else { v = -b } 1452 return []CStmt{ 1453 g.NewCIfStmt(z.Cond, 1454 g.NewCAssignStmt(x, op, g.NewCUnaryExpr(y.Op, z.Then)), 1455 g.toElseStmt(g.NewCAssignStmt(x, op, g.NewCUnaryExpr(y.Op, z.Else))...), 1456 ), 1457 } 1458 } 1459 case *CTernaryExpr: 1460 // v = (x ? a : b) -> if x { v = a } else { v = b } 1461 return []CStmt{ 1462 g.NewCIfStmt(y.Cond, 1463 g.NewCAssignStmt(x, op, y.Then), 1464 g.toElseStmt(g.NewCAssignStmt(x, op, y.Else)...), 1465 ), 1466 } 1467 } 1468 } 1469 if x.CType(nil).Kind().IsPtr() { 1470 switch op { 1471 case BinOpAdd: 1472 if e, ok := y.(*IntToPtr); ok { 1473 y = e.X 1474 } 1475 op = "" 1476 y = cPtrOffset(g.ToPointer(x), y) 1477 case BinOpSub: 1478 if e, ok := y.(*IntToPtr); ok { 1479 y = e.X 1480 } 1481 op = "" 1482 y = g.NewCUnaryExpr(UnaryMinus, y) 1483 y = cPtrOffset(g.ToPointer(x), y) 1484 } 1485 } 1486 return []CStmt{&CAssignStmt{ 1487 g: g, 1488 Left: x, 1489 Op: op, 1490 Right: g.cCast(x.CType(nil), y), 1491 }} 1492 } 1493 1494 type CAssignStmt struct { 1495 g *translator 1496 Left Expr 1497 Op BinaryOp 1498 Right Expr 1499 } 1500 1501 func (s *CAssignStmt) Visit(v Visitor) { 1502 v(s.Left) 1503 v(s.Right) 1504 } 1505 1506 func (s *CAssignStmt) AsStmt() []GoStmt { 1507 x := s.Left.AsExpr() 1508 y := s.Right.AsExpr() 1509 return []GoStmt{ 1510 assignTok(x, s.Op.GoAssignToken(), y), 1511 } 1512 } 1513 1514 func (s *CAssignStmt) Uses() []types.Usage { 1515 var list []types.Usage 1516 list = append(list, types.UseWrite(s.Left)...) 1517 list = append(list, types.UseRead(s.Right)...) 1518 return list 1519 } 1520 1521 type UnusedVar struct { 1522 Name *types.Ident 1523 } 1524 1525 func (s *UnusedVar) Visit(v Visitor) { 1526 } 1527 1528 func (s *UnusedVar) AsStmt() []GoStmt { 1529 return []GoStmt{ 1530 assign(ident("_"), s.Name.GoIdent()), 1531 } 1532 } 1533 1534 func (s *UnusedVar) Uses() []types.Usage { 1535 return []types.Usage{{Ident: s.Name, Access: types.AccessRead}} 1536 } 1537 1538 func stmtCost(stmts ...CStmt) int { 1539 c := 0 1540 for _, s := range stmts { 1541 if s == nil { 1542 continue 1543 } 1544 switch s := s.(type) { 1545 case *CForStmt: 1546 c += 3 + stmtCost(&s.Body) 1547 case *CSwitchStmt: 1548 c++ 1549 for _, s2 := range s.Cases { 1550 c += stmtCost(s2.Stmts...) 1551 } 1552 case *CIfStmt: 1553 c += 1 + stmtCost(s.Then) + stmtCost(s.Else) 1554 case *CCaseStmt: 1555 c += 1 + stmtCost(s.Stmts...) 1556 case *BlockStmt: 1557 c += stmtCost(s.Stmts...) 1558 case *CDeclStmt: 1559 c += 2 1560 default: 1561 c++ 1562 } 1563 } 1564 return c 1565 } 1566 1567 func (g *translator) hasReturns(stmt ...CStmt) bool { 1568 for _, st := range stmt { 1569 if g.isReturnOrExit(st) { 1570 return true 1571 } 1572 switch st := st.(type) { 1573 case *BlockStmt: 1574 if g.hasReturns(st.Stmts...) { 1575 return true 1576 } 1577 case *CIfStmt: 1578 if g.hasReturns(st.Then) { 1579 return true 1580 } 1581 if st.Else != nil { 1582 if g.hasReturns(st.Else) { 1583 return true 1584 } 1585 } 1586 case *CForStmt: 1587 if g.hasReturns(&st.Body) { 1588 return true 1589 } 1590 case *CSwitchStmt: 1591 for _, c := range st.Cases { 1592 if g.hasReturns(c.Stmts...) { 1593 return true 1594 } 1595 } 1596 } 1597 } 1598 return false 1599 } 1600 1601 func (g *translator) hasBreaks(stmt ...CStmt) bool { 1602 for _, st := range stmt { 1603 switch st := st.(type) { 1604 case *CBreakStmt: 1605 return true 1606 case *CGotoStmt: 1607 // TODO: check if it causes exit from the loop 1608 case *BlockStmt: 1609 if g.hasBreaks(st.Stmts...) { 1610 return true 1611 } 1612 case *CIfStmt: 1613 if g.hasBreaks(st.Then) { 1614 return true 1615 } 1616 if st.Else != nil { 1617 if g.hasBreaks(st.Else) { 1618 return true 1619 } 1620 } 1621 } 1622 } 1623 return false 1624 } 1625 1626 func (g *translator) hasBreaksOrReturns(stmt ...CStmt) bool { 1627 for _, st := range stmt { 1628 if g.hasReturns(st) { 1629 return true 1630 } 1631 switch st := st.(type) { 1632 case *CBreakStmt: 1633 return true 1634 case *CGotoStmt: 1635 // TODO: check if it causes exit from the loop 1636 case *BlockStmt: 1637 if g.hasBreaksOrReturns(st.Stmts...) { 1638 return true 1639 } 1640 case *CIfStmt: 1641 if g.hasBreaksOrReturns(st.Then) { 1642 return true 1643 } 1644 if st.Else != nil { 1645 if g.hasBreaksOrReturns(st.Else) { 1646 return true 1647 } 1648 } 1649 case *CForStmt: 1650 // don't consider breaks since it's a new loop 1651 if g.hasReturns(&st.Body) { 1652 return true 1653 } 1654 case *CSwitchStmt: 1655 // don't consider breaks since they affect the switch 1656 for _, c := range st.Cases { 1657 if g.hasReturns(c.Stmts...) { 1658 return true 1659 } 1660 } 1661 } 1662 } 1663 return false 1664 }