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  }