func finalEdits(prog *cc.Prog) { cc.Postorder(prog, func(x cc.Syntax) { switch x := x.(type) { case *cc.Expr: if x.Op == cc.Arrow && x.Text == "prg" && c2go.GoString(x.Left) == "ctxt.arch" { x.Left = x.Left.Left // drop arch. } } }) }
// Rewrite C types to be Go types. func rewriteTypes(prog cc.Syntax) { // Assign overrides to groups. cc.Postorder(prog, func(x cc.Syntax) { if d, ok := x.(*cc.Decl); ok { key := declKey(d) t := override[key] if t == nil { return } if t.Kind == cc.Array { // Override only applies to specific decl. Skip for now. return } f := flowCache[d] if f == nil { return } g := f.group if g.goKind != 0 || g.goType != nil { fmt.Fprintf(os.Stderr, "multiple overrides: %v (%p) and %v (%p)\n", key, f.group, g.goKey, g.goFlow.group) } g.goKey = key g.goFlow = f if t.Kind <= cc.Enum { panic("bad go type override") } if (t.Kind == cc.Ptr || t.Kind == c2go.Slice) && t.Base == nil { g.goKind = t.Kind } else { g.goType = t } } }) // Process overrides. cache := make(map[*cc.Type]*cc.Type) for _, g := range flowGroups { if g.goType != nil { continue } if c2go.Int8 <= g.goKind && g.goKind <= c2go.Float64 { g.goType = &cc.Type{Kind: g.goKind} continue } if g.goKind == cc.Ptr || g.goKind == c2go.Slice { t := g.decls[0].Type if t == nil || t.Base == nil { fmt.Printf("%s: expected ptr/array/slice for %s\n", g.decls[0].Span, declKey(g.decls[0])) continue } g.goType = &cc.Type{Kind: g.goKind, Base: toGoType(nil, nil, t.Base, cache)} continue } if g.goKind != 0 { fmt.Printf("%s: unexpected go kind %v\n", g.goKey, g.goKind) continue } } // Process defaults. // Each group has a 'canonical' instance of the type // that we can use as the initial hint. for _, g := range flowGroups { if g.goType != nil { continue } if g.canon == nil { fmt.Printf("group missing canonical\n") continue } if g.isBool { g.goType = boolType continue } t := g.canon.Def() if cc.Char <= t.Kind && t.Kind <= cc.Enum { // Convert to an appropriately sized number. // Canon is largest rank from C; convert to Go. g.goType = &cc.Type{Kind: c2goKind[t.Kind]} continue } if t.Kind == cc.Ptr || t.Kind == cc.Array { // Default is convert to pointer. // If there are any arrays or any pointer arithmetic, convert to slice instead. k := cc.Ptr for _, d := range g.decls { if d.Type != nil && d.Type.Kind == cc.Array { k = c2go.Slice } } for _, f := range g.syntax { if f.ptrAdd { k = c2go.Slice } } if t.Base.Kind == cc.Char { g.goType = &cc.Type{Kind: c2go.String} continue } g.goType = &cc.Type{Kind: k, Base: toGoType(nil, nil, t.Base, cache)} continue } } if *showGroups { fmt.Printf("%d groups\n", len(flowGroups)) for _, g := range flowGroups { suffix := "" if g.isBool { suffix = " [bool]" } fmt.Printf("group(%d): %v (canon %v)%s\n", len(g.decls), c2go.GoString(g.goType), c2go.GoString(g.canon), suffix) for i := 0; i < 2; i++ { for _, f := range g.syntax { if d, ok := f.syntax.(*cc.Decl); ok == (i == 0) { suffix := "" if ok { suffix = ": " + declKey(d) + " " + c2go.GoString(d.Type) } if f.ptrAdd { suffix += " (ptradd)" } if f.usedAsBool { suffix += " (bool)" } fmt.Printf("\t%s %v%s\n", f.syntax.GetSpan(), f.syntax, suffix) } } } } } // Apply grouped decisions to individual declarations. cc.Postorder(prog, func(x cc.Syntax) { switch x := x.(type) { case *cc.Decl: d := x if d.Name == "..." || d.Type == nil { return } if d.Name == "" && d.Type.Is(cc.Enum) && len(d.Type.Decls) > 0 { for _, dd := range d.Type.Decls { dd.Type = idealType } return } t := override[declKey(d)] if t != nil && t.Kind == cc.Array { d.Type = t return } f := flowCache[d] if f == nil { d.Type = toGoType(nil, d, d.Type, cache) if declKey(d) != "tmp" { fprintf(d.Span, "missing flow group for %s", d.Span, declKey(d)) } return } g := f.group if d.Init != nil && len(d.Init.Braced) > 0 && d.Type != nil && d.Type.Kind == cc.Array { // Initialization of array - do not override type. // But if size is not given explicitly, change to slice. d.Type.Base = toGoType(nil, nil, d.Type.Base, cache) if d.Type.Width == nil { d.Type.Kind = c2go.Slice } return } d.Type = toGoType(g, d, d.Type, cache) if d.Type != nil && d.Type.Kind == cc.Func && d.Type.Base.Kind != cc.Void { if f != nil && f.returnValue != nil && f.returnValue.group != nil && f.returnValue.group.goType != nil { d.Type.Base = f.returnValue.group.goType } } case *cc.Expr: if x.Type != nil { t := toGoType(nil, nil, x.Type, cache) if t == nil { fprintf(x.Span, "cannot convert %v to go type\n", c2go.GoString(x.Type)) } x.Type = t } } }) }
func inferTypes(cfg *Config, prog *cc.Prog) { cc.Postorder(prog, func(x cc.Syntax) { if t, ok := x.(*cc.Type); ok { if t.Kind == cc.Struct || t.Kind == cc.Enum { for _, d := range t.Decls { d.OuterType = t } } } }) addFlow(cfg, prog) for _, f := range flowCache { if f.group != nil { continue } g := &flowGroup{} if f.stopFlow { addToGroup(g, f) continue } exploreGroup(g, f) if len(g.decls) == 0 { continue } flowGroups = append(flowGroups, g) } sort.Sort(flowGroupsBySize(flowGroups)) for _, g := range flowGroups { var typ *cc.Type var typDecl *cc.Decl for _, d := range g.decls { if d.Type == nil { continue } dt := d.Type.Def() if typ == nil || typ.Kind == cc.Ptr && typ.Base.Is(cc.Void) { typ = dt typDecl = d } if !inferCompatible(dt, typ) { fmt.Printf("BAD INFER: mixing %v (%v) and %v (%v)\n", typ, declKey(typDecl), d.Type, declKey(d)) findFlowPath(flowCache[typDecl], flowCache[d]) os.Exit(1) } if isNumericCType(typ) && isNumericCType(dt) && typ.Kind == cc.Enum || dt.Kind != cc.Enum && typ.Kind < dt.Kind { typ = dt typDecl = d } } g.canon = typ g.canonDecl = typDecl } for _, g := range flowGroups { for _, f := range g.syntax { if f.usedAsBool { g.isBool = isNumericCType(g.canon) && g.canon.Kind <= cc.Int break } } } for { changed := false for _, g := range flowGroups { if !g.isBool { continue } for _, f := range g.syntax { x, ok := f.syntax.(*cc.Expr) if !ok { continue } switch x.Op { case cc.EqEq, cc.LtEq, cc.GtEq, cc.NotEq, cc.Lt, cc.Gt, cc.AndAnd, cc.OrOr: continue case cc.Number: if x.Text == "0" || x.Text == "1" { continue } case cc.Call: if x.Left.Op == cc.Name { f := flowCache[x.Left.XDecl] if f != nil && f.returnValue != nil && f.returnValue.group != nil && f.returnValue.group.isBool { continue } } } // can't be bool changed = true g.isBool = false } } if !changed { break } } if *src != "" && *dst != "" { var fsrc, fdst *flowSyntax for _, f := range flowCache { d, ok := f.syntax.(*cc.Decl) if ok { key := declKey(d) if key == "" { continue } if *src == key { fsrc = f fmt.Printf("%s in %p %p\n", key, f, f.group) } if strings.HasSuffix(*src, key) { fmt.Printf("near: %s\n", key) } if *dst == key { fdst = f fmt.Printf("%s in %p %p\n", key, f, f.group) } if strings.HasSuffix(*dst, key) { fmt.Printf("near: %s\n", key) } } } if fsrc != nil && fdst != nil { findFlowPath(fsrc, fdst) os.Exit(0) } fmt.Printf("%s and %s are not in the same group\n", *src, *dst) os.Exit(0) } }
func rewriteLen(cfg *Config, prog *cc.Prog) { cc.Postorder(prog, func(x cc.Syntax) { switch x := x.(type) { case *cc.Expr: // Assign to len, generated by len rewrite. Change to reslice. if x.Op == cc.Eq && x.Left.Op == cc.Call && x.Left.Left.Op == cc.Name && x.Left.Left.Text == "len" && len(x.Left.List) > 0 { arg := x.Left.List[0] x.Left = arg x.Right = &cc.Expr{ Op: c2go.ExprSlice, List: []*cc.Expr{arg, nil, x.Right}, } return } if (x.Op == cc.Arrow || x.Op == cc.Dot) && x.XDecl != nil { k := declKey(x.XDecl) name := cfg.Len[k] if name == "" { return } d := x.XDecl if d.OuterType == nil { fmt.Fprintf(os.Stderr, "found use of %s but missing type\n", k) return } t := d.OuterType var other *cc.Decl for _, dd := range t.Decls { if dd.Name == name { other = dd break } } if other == nil { fmt.Fprintf(os.Stderr, "found use of %s but cannot find field %s\n", k, name) return } left := x.Left x.Op = cc.Call x.Left = &cc.Expr{ Op: cc.Name, Text: "len", XType: &cc.Type{Kind: cc.Func, Base: intType}, } x.List = []*cc.Expr{ { Op: cc.Dot, Left: left, Text: name, XDecl: other, }, } } } }) cc.Postorder(prog, func(x cc.Syntax) { switch x := x.(type) { case *cc.Type: out := x.Decls[:0] for _, d := range x.Decls { k := declKey(d) if cfg.Len[k] == "" && !cfg.Delete[k] { out = append(out, d) } } x.Decls = out } }) }
// 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 } } }) }