github.com/gotranspile/cxgo@v0.3.7/postproc.go (about) 1 package cxgo 2 3 import "github.com/gotranspile/cxgo/types" 4 5 func (g *translator) adaptMain(decl []CDecl) []CDecl { 6 for _, d := range decl { 7 f, ok := d.(*CFuncDecl) 8 if !ok || f.Body == nil { 9 continue 10 } 11 switch f.Name.Name { 12 case "main": 13 g.translateMain(f) 14 } 15 } 16 return decl 17 } 18 19 func (g *translator) flatten(decl []CDecl) { 20 for _, d := range decl { 21 f, ok := d.(*CFuncDecl) 22 if !ok || f.Body == nil { 23 continue 24 } 25 flatten := g.conf.FlattenAll 26 if c, ok := g.idents[f.Name.Name]; ok && c.Flatten != nil { 27 flatten = *c.Flatten 28 } 29 if !flatten { 30 continue 31 } 32 cf := g.NewControlFlow(f.Body.Stmts) 33 f.Body.Stmts = cf.Flatten() 34 } 35 } 36 37 func (g *translator) fixUnusedVars(decl []CDecl) { 38 for _, d := range decl { 39 switch d := d.(type) { 40 case *CFuncDecl: 41 g.fixUnusedVarsBlock(d.Body) 42 } 43 } 44 } 45 46 func (g *translator) fixUnusedVarsBlock(b *BlockStmt) { 47 if b != nil { 48 b.Stmts = g.fixUnusedVarsStmts(b.Stmts) 49 } 50 } 51 52 func (g *translator) fixUnusedVarsStmts(stmts []CStmt) []CStmt { 53 for i := 0; i < len(stmts); i++ { 54 st := stmts[i] 55 var next []CStmt 56 if i != len(stmts)-1 { 57 next = stmts[i+1:] 58 } 59 add := g.unusedVarsStmt(st, next) 60 if len(add) == 0 { 61 continue 62 } 63 arr := make([]CStmt, 0, len(stmts)+len(add)) 64 arr = append(arr, stmts[:i+1]...) 65 arr = append(arr, add...) 66 arr = append(arr, next...) 67 stmts = arr 68 i += len(add) 69 } 70 return stmts 71 } 72 73 func (g *translator) unusedVarsStmt(st CStmt, next []CStmt) []CStmt { 74 switch st := st.(type) { 75 case *BlockStmt: 76 g.fixUnusedVarsBlock(st) 77 return nil 78 case *CForStmt: 79 g.fixUnusedVarsBlock(&st.Body) 80 return nil 81 case *CIfStmt: 82 g.fixUnusedVarsBlock(st.Then) 83 g.unusedVarsStmt(st.Else, nil) 84 return nil 85 case *CDeclStmt: 86 vd, ok := st.Decl.(*CVarDecl) 87 if !ok || vd.Const { 88 return nil 89 } 90 var add []CStmt 91 if len(next) == 0 { 92 // everything in the last declaration is always unused 93 for _, name := range vd.Names { 94 if name.Name == "__func__" { 95 continue 96 } 97 add = append(add, &UnusedVar{Name: name}) 98 } 99 return add 100 } 101 for i, name := range vd.Names { 102 if name.Name == "__func__" { 103 continue 104 } 105 used := false 106 if i+1 < len(vd.Inits) { 107 // we should scan inits of following variables in this block 108 for _, e := range vd.Inits[i+1:] { 109 for _, u := range types.UseRead(e) { 110 if name == u.Ident && u.Access == types.AccessRead { 111 used = true 112 break 113 } 114 } 115 } 116 } 117 if !used { 118 for _, st := range next { 119 for _, u := range st.Uses() { 120 if name == u.Ident && u.Access == types.AccessRead { 121 used = true 122 break 123 } 124 } 125 } 126 } 127 if !used { 128 add = append(add, &UnusedVar{Name: name}) 129 } 130 } 131 return add 132 default: 133 return nil 134 } 135 } 136 137 func (g *translator) fixImplicitReturns(decl []CDecl) { 138 for _, d := range decl { 139 switch d := d.(type) { 140 case *CFuncDecl: 141 ret := d.Type.Return() 142 if ret == nil || d.Body == nil { 143 continue 144 } 145 d.Body.Stmts = g.fixImplicitReturnStmts(ret, d.Body.Stmts) 146 } 147 } 148 } 149 150 func (g *translator) fixImplicitReturnStmts(ret types.Type, stmts []CStmt) []CStmt { 151 if len(stmts) == 0 { 152 return g.NewZeroReturnStmt(ret) 153 } 154 last := stmts[len(stmts)-1] 155 if g.isHardJump(last) { 156 return stmts 157 } 158 if !g.fixImplicitReturnStmt(ret, last) { 159 stmts = append(stmts, g.NewZeroReturnStmt(ret)...) 160 } 161 return stmts 162 } 163 164 func (g *translator) fixImplicitReturnStmt(ret types.Type, st CStmt) bool { 165 if g.isReturnOrExit(st) { 166 return true 167 } 168 switch st := st.(type) { 169 case *CGotoStmt: 170 return true 171 case *BlockStmt: 172 st.Stmts = g.fixImplicitReturnStmts(ret, st.Stmts) 173 return true 174 case *CIfStmt: 175 if st.Else == nil { 176 return false 177 } 178 if !g.fixImplicitReturnStmt(ret, st.Else) { 179 return false 180 } 181 st.Then.Stmts = g.fixImplicitReturnStmts(ret, st.Then.Stmts) 182 return true 183 case *CForStmt: 184 return !g.forCanBreak(st) 185 case *CSwitchStmt: 186 return !g.switchCanBreak(st) 187 default: 188 return false 189 } 190 } 191 192 func (g *translator) rewriteStatements(decl []CDecl) { 193 for _, d := range decl { 194 f, ok := d.(*CFuncDecl) 195 if !ok || f.Body == nil { 196 continue 197 } 198 g.rewriteStmts(f.Body.Stmts) 199 } 200 } 201 202 func (g *translator) rewriteStmts(stmts []CStmt) { 203 for i, st := range stmts { 204 if s, ok := g.rewriteStmt(st); ok { 205 stmts[i] = s 206 } 207 } 208 } 209 210 func (g *translator) rewriteStmt(st CStmt) (CStmt, bool) { 211 switch st := st.(type) { 212 case *CExprStmt: 213 if c, ok := st.Expr.(*CallExpr); ok { 214 if id, ok := c.Fun.(Ident); ok { 215 switch id.Identifier() { 216 case g.env.C().FreeFunc(): 217 if len(c.Args) == 1 && canAssignTo(c.Args[0]) { 218 return g.NewCAssignStmtP(c.Args[0], "", g.Nil()), true 219 } 220 case g.env.C().AssertFunc(): 221 if len(c.Args) == 1 { 222 return g.NewCIfStmt(g.cNot(c.Args[0]), NewCExprStmt( 223 &CallExpr{Fun: FuncIdent{g.env.Go().PanicFunc()}, Args: []Expr{g.stringLit("assert failed")}}, 224 ), nil), true 225 } 226 } 227 } 228 } 229 case *BlockStmt: 230 g.rewriteStmts(st.Stmts) 231 case *CIfStmt: 232 g.rewriteStmts(st.Then.Stmts) 233 if st.Else != nil { 234 if e, ok := g.rewriteStmt(st.Else); ok { 235 st.Else = g.toElseStmt(e) 236 } 237 } 238 case *CForStmt: 239 g.rewriteStmts(st.Body.Stmts) 240 case *CSwitchStmt: 241 for _, c := range st.Cases { 242 g.rewriteStmts(c.Stmts) 243 } 244 } 245 return st, false 246 }