func addFlow(cfg *Config, prog *cc.Prog) { for _, d := range prog.Decls { addFlowDecl(cfg, nil, d) } // Mop up the rest. cc.Preorder(prog, func(x cc.Syntax) { if d, ok := x.(*cc.Decl); ok { addFlowDecl(cfg, nil, d) } }) }
func fixBools(prog *cc.Prog) { cc.Preorder(prog, func(x cc.Syntax) { switch x := x.(type) { case *cc.Expr: if x.Op == cc.Not { switch x.Left.Op { case cc.OrOr, cc.AndAnd, cc.EqEq, cc.NotEq, cc.LtEq, cc.GtEq, cc.Lt, cc.Gt, cc.Paren: x.Left = negate(x.Left) fixMerge(x, x.Left) } } } }) }
func doExports(cfg *Config, prog *cc.Prog) { for _, d := range cfg.TopDecls { if shouldExport(cfg, d.Name) { exportDecl(d) } pkg := d.GoPackage if pkg == "" { continue } cc.Preorder(d, func(x cc.Syntax) { switch x := x.(type) { case *cc.Expr: if x.Op == cc.Name && x.XDecl != nil && x.XDecl.GoPackage != "" && x.XDecl.GoPackage != pkg { exportDecl(x.XDecl) } case *cc.Type: if x.Kind == cc.TypedefType && x.TypeDecl != nil && x.TypeDecl.GoPackage != "" && x.TypeDecl.GoPackage != pkg { exportDecl(x.TypeDecl) x.Name = x.TypeDecl.Name } } }) } }
func fixQsortCmp(decl *cc.Decl) *cc.Type { ftyp := decl.Type if ftyp.Kind != cc.Func || len(ftyp.Decls) != 2 || !isGoVoidPtr(ftyp.Decls[0].Type) || !isGoVoidPtr(ftyp.Decls[1].Type) { fprintf(decl.Span, "invalid qsort cmp function %v - wrong args", c2go.GoString(ftyp)) return nil } a1, a2 := ftyp.Decls[0], ftyp.Decls[1] var eq1, eq2, p1, p2 *cc.Expr var indir1, indir2 bool cc.Preorder(decl.Body, func(x cc.Syntax) { switch x := x.(type) { case *cc.Expr: if x.Op != cc.Eq { return } r := x.Right if r.Op == cc.Indir { r = r.Left } if r.Op == cc.Cast && r.Left.Op == cc.Name { if r.Left.XDecl == a1 && p1 == nil { p1 = x.Left eq1 = x indir1 = r != x.Right } if r.Left.XDecl == a2 && p2 == nil { p2 = x.Left eq2 = x indir1 = r != x.Right } } } }) if p1 == nil || p2 == nil { fprintf(decl.Span, "invalid qsort cmp function - cannot find arg extraction") return nil } if !sameType(p1.XType, p2.XType) { fprintf(decl.Span, "invalid qsort cmp function - different arg types %v and %v", c2go.GoString(p1.XType), c2go.GoString(p2.XType)) return nil } if indir1 != indir2 { fprintf(decl.Span, "invalid qsort cmp function - different arg indirection") return nil } typ := p1.XType if !indir1 { if typ.Def().Kind != cc.Ptr { fprintf(decl.Span, "invalid qsort cmp function - arg ptr cast to non-ptr %v", c2go.GoString(typ)) return nil } typ = typ.Def().Base } // Have all the information. Committed. // Rewrite to take x, i, j, use x[i] for p1, x[j] for p2, // take address of x[i], x[j] if there was no indirect, // replace all return z with return z < 0. cmp := decl.Name decl.Name = "(x " + cmp + ") Less" decl.Type = &cc.Type{ Kind: cc.Func, Base: boolType, Decls: []*cc.Decl{ {Name: "i", Type: &cc.Type{Kind: cc.TypedefType}}, {Name: "j", Type: intType}, }, } prefix := "" if !indir1 { prefix = "&" } eq1.Right = &cc.Expr{Op: cc.Name, Text: prefix + "x[i]", XType: p1.XType} eq2.Right = &cc.Expr{Op: cc.Name, Text: prefix + "x[j]", XType: p1.XType} cc.Preorder(decl.Body, func(x cc.Syntax) { switch x := x.(type) { case *cc.Stmt: if x.Op == cc.Return && x.Expr != nil { ret := x.Expr // Pick off 0, -1, +1. // Otherwise rewrite ret to ret < 0. switch ret.Op { case cc.Minus, cc.Plus: if ret.Left.Op == cc.Number { ret.Op = cc.Name if ret.Op == cc.Plus { ret.Text = "true" } else { ret.Text = "false" } ret.Left = nil ret.XType = boolType return } case cc.Number: ret.Op = cc.Name ret.Text = "false" ret.XType = boolType return } x.Expr = &cc.Expr{Op: cc.Lt, Left: ret, Right: &cc.Expr{Op: cc.Number, Text: "0"}, XType: boolType} return } } }) return typ }
// renameDecls renames file-local declarations to make them // unique across the whole set of files being considered. // For now, it appends the file base name to the declared name. // Eventually it could be smarter and not do that when not necessary. // It also renames names like 'type' and 'func' to avoid Go keywords. func renameDecls(cfg *Config, prog *cc.Prog) { // Rewrite C identifiers to avoid important Go words (keywords, iota, etc). cc.Preorder(prog, func(x cc.Syntax) { switch x := x.(type) { case *cc.Decl: if k := goKeyword[x.Name]; k != "" { x.Name = k } case *cc.Stmt: for _, lab := range x.Labels { if k := goKeyword[lab.Name]; k != "" { lab.Name = k } } switch x.Op { case cc.Goto: if k := goKeyword[x.Text]; k != "" { x.Text = k } } } }) // Build list of declared top-level names. // Not just prog.Decls because of enums and struct definitions. typedefs := map[*cc.Type]bool{} for _, d := range prog.Decls { if d.Storage&cc.Typedef != 0 { typedefs[d.Type] = true } } var decls []*cc.Decl for _, d := range prog.Decls { if d.Name == "" { if typedefs[d.Type] { continue } switch d.Type.Kind { case cc.Struct: if d.Type.Tag != "" { decls = append(decls, d) d.Name = d.Type.Tag d.Storage = cc.Typedef } if d.Type.TypeDecl == nil { d.Type.TypeDecl = d } case cc.Enum: d.Type.Tag = "" // enum tags are worthless for _, dd := range d.Type.Decls { decls = append(decls, dd) } } continue } decls = append(decls, d) if d.Storage&cc.Typedef != 0 && d.Type != nil && d.Type.TypeDecl == nil { d.Type.TypeDecl = d } } // Assign declarations to packages and identify conflicts. count := make(map[string]int) src := make(map[string]string) for _, d := range decls { pkg := findPkg(cfg, d.Span.Start.File) if pkg == "" { continue } d.GoPackage = pkg key := d.GoPackage + "." + d.Name if count[key]++; count[key] > 1 { fprintf(d.Span, "conflicting name %s in %s (last at %s)", d.Name, pkg, src[key]) continue } src[key] = fmt.Sprintf("%s:%d", d.Span.Start.File, d.Span.Start.Line) } // Rename static, conflicting names. for _, d := range decls { key := d.GoPackage + "." + d.Name if count[key] > 1 { file := filepath.Base(d.Span.Start.File) if i := strings.Index(file, "."); i >= 0 { file = file[:i] } d.Name += "_" + file } } cfg.TopDecls = decls }
// Rewrite from C constructs to Go constructs. func rewriteSyntax(prog *cc.Prog) { cc.Preorder(prog, func(x cc.Syntax) { switch x := x.(type) { case *cc.Stmt: rewriteStmt(x) case *cc.Expr: switch x.Op { case cc.Number: // Rewrite char literal. // In general we'd need to rewrite all string and char literals // but these are the only forms that comes up. switch x.Text { case `'\0'`: x.Text = `'\x00'` case `'\"'`: x.Text = `'"'` } case cc.Paren: switch x.Left.Op { case cc.Number, cc.Name: fixMerge(x, x.Left) } case cc.OrEq, cc.AndEq, cc.Or, cc.Eq, cc.EqEq, cc.NotEq, cc.LtEq, cc.GtEq, cc.Lt, cc.Gt: cutParen(x, cc.Or, cc.And, cc.Lsh, cc.Rsh) } case *cc.Type: // Rewrite int f(void) to int f(). if x.Kind == cc.Func && len(x.Decls) == 1 && x.Decls[0].Name == "" && x.Decls[0].Type.Is(cc.Void) { x.Decls = nil } } }) // Apply changed struct tags to typedefs. // Excise dead pieces. cc.Postorder(prog, func(x cc.Syntax) { switch x := x.(type) { case *cc.Type: if x.Kind == cc.TypedefType && x.Base != nil && x.Base.Tag != "" { x.Name = x.Base.Tag } case *cc.Stmt: if x.Op == cc.StmtExpr && x.Expr.Op == cc.Comma && len(x.Expr.List) == 0 { x.Op = cc.Empty } x.Block = filterBlock(x.Block) case *cc.Expr: if x.Op == c2go.ExprBlock { x.Block = filterBlock(x.Block) } switch x.Op { case cc.Add, cc.Sub: // Turn p + y - z, which is really (p + y) - z, into p + (y - z), // so that there is only one pointer addition (which will turn into // a slice operation using y-z as the index). if x.XType != nil && x.XType.Kind == cc.Ptr { switch x.Left.Op { case cc.Add, cc.Sub: if x.Left.XType != nil && x.Left.XType.Kind == cc.Ptr { p, op1, y, op2, z := x.Left.Left, x.Left.Op, x.Left.Right, x.Op, x.Right if op1 == cc.Sub { y = &cc.Expr{Op: cc.Minus, Left: y, XType: y.XType} } x.Op = cc.Add x.Left = p x.Right = &cc.Expr{Op: op2, Left: y, Right: z, XType: x.XType} } } } } // Turn c + p - q, which is really (c + p) - q, into c + (p - q), // so that there is no int + ptr addition, only a ptr - ptr subtraction. if x.Op == cc.Sub && x.Left.Op == cc.Add && !isPtrOrArray(x.XType) && isPtrOrArray(x.Left.XType) && !isPtrOrArray(x.Left.Left.XType) { c, p, q := x.Left.Left, x.Left.Right, x.Right expr := x.Left expr.Left = p expr.Right = q expr.Op = cc.Sub x.Op = cc.Add x.Left = c x.Right = expr expr.XType = x.XType } } }) }
func fixPrintFormat(curfn *cc.Decl, fx *cc.Expr, args []*cc.Expr) { for _, arg := range args { fixGoTypesExpr(curfn, arg, nil) cc.Preorder(arg, func(x cc.Syntax) { if x, ok := x.(*cc.Expr); ok && x.Op == cc.Name && strings.HasPrefix(x.Text, "bigP") { x.Text = "p" x.XDecl = nil } }) } format, err := strconv.Unquote(fx.Texts[0]) if err != nil { fprintf(fx.Span, "cannot parse quoted string: %v", err) return } suffix := "" var buf bytes.Buffer start := 0 narg := 0 for i := 0; i < len(format); i++ { if format[i] != '%' { continue } buf.WriteString(format[start:i]) start = i i++ for i < len(format) { c := format[i] switch c { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '#', '-', '.', ',', ' ', 'h', 'l', 'u': i++ continue } break } if i >= len(format) { fprintf(fx.Span, "print format ends mid-verb") return } flags, verb := format[start:i], format[i] start = i + 1 allFlags := flags _ = allFlags flags = strings.Replace(flags, "h", "", -1) flags = strings.Replace(flags, "l", "", -1) flags = strings.Replace(flags, "u", "", -1) if j := strings.Index(flags, "#0"); j >= 0 && verb == 'x' { k := j + 2 for k < len(flags) && '0' <= flags[k] && flags[k] <= '9' { k++ } n, _ := strconv.Atoi(flags[j+2 : k]) flags = flags[:j+2] + fmt.Sprint(n-2) + flags[k:] } convert := "" switch verb { default: fprintf(fx.Span, "unrecognized format %s%c", flags, verb) buf.WriteString("%") buf.WriteString(flags) buf.WriteString(string(verb)) case 'f', 'e', 'E', 'g', 'G', 's', 'c', 'p': // usual meanings buf.WriteString(flags) buf.WriteString(string(verb)) case 'x', 'X', 'o', 'd', 'b': // usual meanings, but force unsigned if u is given buf.WriteString(flags) buf.WriteString(string(verb)) if narg >= len(args) || !strings.Contains(allFlags, "u") { break } arg := args[narg] if t := arg.XType.Def(); t != nil { switch t.Kind { case c2go.Int8: convert = "uint8" case c2go.Int16: convert = "uint16" case c2go.Int32: convert = "uint32" case c2go.Int64: convert = "uint64" case c2go.Int: convert = "uint" } } case 'A': // address if allFlags != "%" { fprintf(fx.Span, "format %s%c", allFlags, verb) } buf.WriteString("%v") convert = "Aconv" + suffix case 'L': if allFlags != "%" { fprintf(fx.Span, "format %s%c", allFlags, verb) } buf.WriteString("%v") if narg >= len(args) { break } arg := args[narg] if (arg.Op == cc.Dot || arg.Op == cc.Arrow) && arg.Text == "lineno" { arg.Text = "Line" arg.XDecl = nil args[narg] = &cc.Expr{Op: cc.Call, Left: arg, XType: stringType} break } convert = "ctxt.Line" case '@': if allFlags != "%" { fprintf(fx.Span, "format %s%c", allFlags, verb) } buf.WriteString("%v") convert = "RAconv" + suffix case 'D': if allFlags != "%" && allFlags != "%l" { fprintf(fx.Span, "format %s%c", allFlags, verb) } buf.WriteString("%v") if narg >= len(args) { break } flag := &cc.Expr{Op: cc.Name, Text: "0"} if strings.Contains(allFlags, "l") { flag.Text = "fmtLong" } arg := args[narg] args[narg] = &cc.Expr{ Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "Dconv" + suffix}, List: []*cc.Expr{ &cc.Expr{Op: cc.Name, Text: "p"}, flag, arg, }, XType: stringType, } case 'M': if allFlags != "%" { fprintf(fx.Span, "format %s%c", allFlags, verb) } buf.WriteString("%v") convert = "Mconv" + suffix case 'R': if allFlags != "%" { fprintf(fx.Span, "format %s%c", allFlags, verb) } buf.WriteString("%v") convert = "Rconv" + suffix case '$': if allFlags != "%" { fprintf(fx.Span, "format %s%c", allFlags, verb) } buf.WriteString("%q") case 'P': if allFlags != "%" { fprintf(fx.Span, "format %s%c", allFlags, verb) } buf.WriteString("%v") } if convert != "" && narg < len(args) { arg := args[narg] args[narg] = &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: convert}, List: []*cc.Expr{arg}, XType: stringType} } if verb != 'r' { narg++ } } buf.WriteString(format[start:]) fx.Texts[0] = strconv.Quote(buf.String()) }
func fixFormatter(fn *cc.Decl) { // Find va_arg assignment. var arg *cc.Expr var argType *cc.Type var ps []*cc.Expr cc.Preorder(fn.Body, func(x cc.Syntax) { switch x := x.(type) { case *cc.Expr: if x.Op == cc.Name && strings.HasPrefix(x.Text, "bigP") { ps = append(ps, x) } case *cc.Stmt: stmt := x if stmt.Op != cc.StmtExpr { return } expr := stmt.Expr if expr.Op != cc.Eq { return } if expr.Left.Op == cc.Name && strings.HasPrefix(expr.Left.Text, "bigP") { stmt.Op = cc.Empty stmt.Expr = nil return } if expr.Op != cc.Eq || expr.Right.Op != cc.VaArg { return } if arg != nil { fprintf(fn.Span, "multiple va_arg in formatter") } arg = expr.Left argType = expr.Right.Type stmt.Op = cc.Empty } }) if arg == nil { fprintf(fn.Span, "missing va_arg in formatter") return } fp := fn.Type.Decls[0] fp.Type = stringType fn.Type.Decls[0] = arg.XDecl fn.Type.Base = stringType if strings.HasPrefix(fn.Name, "Dconv") && len(ps) > 0 { pd := &cc.Decl{Name: "p", Type: ps[0].XDecl.Type} fd := &cc.Decl{Name: "flag", Type: intType} for _, p := range ps { p.XDecl = pd } fn.Type.Decls = []*cc.Decl{ pd, fd, arg.XDecl, } } cc.Preorder(fn.Body, func(x cc.Syntax) { switch x := x.(type) { case *cc.Stmt: if x.Op == cc.StmtDecl && x.Decl == arg.XDecl { x.Decl = fp } if x.Op == cc.Return && x.Expr != nil && x.Expr.Text == "0" { x.Expr = &cc.Expr{Op: cc.Name, Text: fp.Name, XDecl: fp} } case *cc.Expr: if x.Op == cc.Arrow && x.Text == "flags" { x.Op = cc.Name x.Text = "flag" x.XDecl = nil x.Left = nil } if x.Op == cc.Name { switch x.Text { case "FmtLong": x.Text = "fmtLong" x.XDecl = nil } } } }) }