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. } } }) }
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 }
func fixGoTypesExpr(fn *cc.Decl, x *cc.Expr, targ *cc.Type) (ret *cc.Type) { if x == nil { return nil } defer func() { x.XType = ret }() if x.Op == cc.Paren { return fixGoTypesExpr(fn, x.Left, targ) } // Make explicit C's implicit conversions from boolean to non-boolean and vice versa. switch x.Op { case cc.AndAnd, cc.OrOr, cc.Not, cc.EqEq, cc.Lt, cc.LtEq, cc.Gt, cc.GtEq, cc.NotEq: if targ != nil && targ.Kind != c2go.Bool { old := copyExpr(x) if targ.Kind == c2go.Int { x.Op = cc.Call x.Left = &cc.Expr{Op: cc.Name, Text: "bool2int"} x.List = []*cc.Expr{old} x.Right = nil } else { x.Op = cc.Cast x.Left = &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "bool2int"}, List: []*cc.Expr{old}} x.Type = targ } fixGoTypesExpr(fn, old, boolType) return targ } default: if targ != nil && targ.Kind == c2go.Bool { old := copyExpr(x) left := fixGoTypesExpr(fn, old, nil) if left != nil && left.Kind == c2go.Bool { return targ } if old.Op == cc.Number { switch old.Text { case "1": x.Op = cc.Name x.Text = "true" return targ case "0": x.Op = cc.Name x.Text = "false" return targ } } x.Op = cc.NotEq x.Left = old x.Right = zeroFor(left) return targ } } switch x.Op { default: panic(fmt.Sprintf("unexpected construct %v in fixGoTypesExpr - %v - %v", c2go.GoString(x), x.Op, x.Span)) case c2go.ExprSlice: // inserted by rewriteLen left := fixGoTypesExpr(fn, x.List[0], targ) fixGoTypesExpr(fn, x.List[1], nil) fixGoTypesExpr(fn, x.List[2], nil) return left case cc.Comma: for i, y := range x.List { t := targ if i+1 < len(x.List) { t = nil } fixGoTypesExpr(fn, y, t) } return nil case c2go.ExprBlock: for _, stmt := range x.Block { fixGoTypesStmt(nil, fn, stmt) } return nil case cc.Add, cc.And, cc.Div, cc.Mod, cc.Mul, cc.Or, cc.Sub, cc.Xor: if x.Op == cc.Sub && isPtrSliceOrArray(x.Left.XType) && isPtrSliceOrArray(x.Right.XType) { left := fixGoTypesExpr(fn, x.Left, nil) right := fixGoTypesExpr(fn, x.Right, nil) if left != nil && right != nil && left.Kind != right.Kind { if left.Kind == c2go.Slice { forceConvert(fn, x.Right, right, left) } else { forceConvert(fn, x.Left, left, right) } } x.Left = &cc.Expr{Op: cc.Minus, Left: &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "cap"}, List: []*cc.Expr{x.Left}}} x.Right = &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "cap"}, List: []*cc.Expr{x.Right}} x.Op = cc.Add return intType } left := fixGoTypesExpr(fn, x.Left, targ) if x.Op == cc.And && x.Right.Op == cc.Twid { x.Op = c2go.AndNot x.Right = x.Right.Left } if x.Op == cc.Add && isSliceStringOrArray(left) { fixGoTypesExpr(fn, x.Right, nil) x.Op = c2go.ExprSlice x.List = []*cc.Expr{x.Left, x.Right, nil} x.Left = nil x.Right = nil if left.Kind == cc.Array { left = &cc.Type{Kind: c2go.Slice, Base: left.Base} } return left } right := fixGoTypesExpr(fn, x.Right, targ) return fixBinary(fn, x, left, right, targ) case cc.AddEq, cc.AndEq, cc.DivEq, cc.Eq, cc.ModEq, cc.MulEq, cc.OrEq, cc.SubEq, cc.XorEq: left := fixGoTypesExpr(fn, x.Left, nil) if x.Op == cc.AndEq && x.Right.Op == cc.Twid { x.Op = c2go.AndNotEq x.Right = x.Right.Left } if x.Op == cc.AddEq && isSliceOrString(left) { fixGoTypesExpr(fn, x.Right, nil) old := copyExpr(x.Left) x.Op = cc.Eq x.Right = &cc.Expr{Op: c2go.ExprSlice, List: []*cc.Expr{old, x.Right, nil}} return left } if x.Op == cc.Eq && x.Left.Op == cc.Index && sameType(x.Left.Left.XType, stringType) && c2go.GoString(x.Left.Right) == "0" && c2go.GoString(x.Right) == "0" { x.Left = x.Left.Left x.Right = &cc.Expr{Op: cc.Name, Text: `""`} return x.Left.XType } forceGoType(fn, x.Right, left) return left case c2go.ColonEq: left := fixGoTypesExpr(fn, x.Right, nil) x.Left.XType = left x.Left.XDecl.Type = left return left case cc.Addr: left := fixGoTypesExpr(fn, x.Left, nil) if left == nil { return nil } if targ != nil && targ.Kind == c2go.Slice && sameType(targ.Base, left) { l := x.Left l.Op = c2go.ExprSlice l.List = []*cc.Expr{l.Left, l.Right, nil} l.Left = nil l.Right = nil fixMerge(x, l) return targ } return &cc.Type{Kind: cc.Ptr, Base: left} case cc.AndAnd, cc.OrOr, cc.Not: fixGoTypesExpr(fn, x.Left, boolType) if x.Right != nil { fixGoTypesExpr(fn, x.Right, boolType) } return boolType case cc.Arrow, cc.Dot: left := fixGoTypesExpr(fn, x.Left, nil) if x.Op == cc.Arrow && isSliceOrString(left) { x.Left = &cc.Expr{Op: cc.Index, Left: x.Left, Right: &cc.Expr{Op: cc.Number, Text: "0"}} } return x.XDecl.Type case cc.Call: if fixPrintf(fn, x) { return x.XType } if fixSpecialCall(fn, x, targ) { return x.XType } left := fixGoTypesExpr(fn, x.Left, nil) for i, y := range x.List { if left != nil && left.Kind == cc.Func && i < len(left.Decls) { forceGoType(fn, y, left.Decls[i].Type) } else { fixGoTypesExpr(fn, y, nil) } } if left != nil && left.Kind == cc.Func { return left.Base } return nil case cc.Cast: fixGoTypesExpr(fn, x.Left, nil) return x.Type case cc.CastInit: fixGoTypesInit(nil, x.Init) return x.Type case cc.EqEq, cc.Gt, cc.GtEq, cc.Lt, cc.LtEq, cc.NotEq: if fixSpecialCompare(fn, x) { return boolType } left := fixGoTypesExpr(fn, x.Left, nil) if x.Right.Op == cc.Number && x.Right.Text == "0" { if isSliceOrPtr(left) { x.Right.Op = cc.Name x.Right.Text = "nil" return boolType } if left != nil && left.Kind == c2go.String { x.Right.Op = cc.String x.Right.Texts = []string{`""`} return boolType } } right := fixGoTypesExpr(fn, x.Right, nil) if isSliceOrArray(x.Left.XType) && isSliceOrArray(x.Right.XType) { x.Left = &cc.Expr{Op: cc.Minus, Left: &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "cap"}, List: []*cc.Expr{x.Left}}} x.Right = &cc.Expr{Op: cc.Minus, Left: &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "cap"}, List: []*cc.Expr{x.Right}}} return boolType } fixBinary(fn, x, left, right, nil) return boolType case cc.Index, cc.Indir: left := fixGoTypesExpr(fn, x.Left, nil) if x.Right != nil { fixGoTypesExpr(fn, x.Right, nil) } if left == nil { return nil } if isSliceOrString(left) && x.Op == cc.Indir { x.Op = cc.Index x.Right = &cc.Expr{Op: cc.Number, Text: "0"} } switch left.Kind { case cc.Ptr, c2go.Slice, cc.Array: return left.Base case c2go.String: return byteType } return nil case cc.Lsh, cc.Rsh: left := fixGoTypesExpr(fn, x.Left, targ) if left != nil && targ != nil && c2go.Int8 <= left.Kind && left.Kind <= c2go.Float64 && targ.Kind > left.Kind { forceConvert(fn, x.Left, left, targ) left = targ } fixShiftCount(fn, x.Right) return left case cc.LshEq, cc.RshEq: left := fixGoTypesExpr(fn, x.Left, nil) fixShiftCount(fn, x.Right) return left case cc.Name: if x.Text == "nelem" { } switch x.Text { case "T", "S", "N", "L", "P": x.Text = "nil" x.XDecl = nil return nil case "nelem": x.Text = "len" x.XDecl = nil fallthrough case "len": return &cc.Type{Kind: cc.Func, Base: intType} } if x.XDecl == nil { return nil } return x.XDecl.Type case cc.Number: return idealType case cc.Minus, cc.Plus, cc.Twid: return fixGoTypesExpr(fn, x.Left, targ) case cc.Offsetof: // TODO return nil case cc.Paren: return fixGoTypesExpr(fn, x.Left, targ) case cc.PostDec, cc.PostInc: left := fixGoTypesExpr(fn, x.Left, nil) if x.Op == cc.PostInc && isSliceOrString(left) { old := copyExpr(x.Left) x.Op = cc.Eq x.Right = &cc.Expr{Op: c2go.ExprSlice, List: []*cc.Expr{old, &cc.Expr{Op: cc.Number, Text: "1"}, nil}} } return nil case cc.SizeofExpr: left := fixGoTypesExpr(fn, x.Left, nil) if left != nil && (left.Kind == cc.Array || left.Kind == c2go.Slice) && left.Base.Def().Is(c2go.Uint8) { x.Op = cc.Call x.List = []*cc.Expr{x.Left} x.Left = &cc.Expr{Op: cc.Name, Text: "len"} return intType } return nil case cc.SizeofType: return nil case cc.String: return &cc.Type{Kind: c2go.String} case cc.VaArg: // TODO return nil } }
// 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 fixSpecialCall(fn *cc.Decl, x *cc.Expr, targ *cc.Type) bool { if x.Left.Op != cc.Name { return false } switch x.Left.Text { case "memmove": if len(x.List) != 3 { fprintf(x.Span, "unsupported %v", x) return false } siz := x.List[2] if siz.Op == cc.Number && siz.Text == "4" { obj1, obj1Type := objIndir(fn, x.List[0]) obj2, obj2Type := objIndir(fn, x.List[1]) if obj1Type == nil || obj2Type == nil { fprintf(x.Span, "unsupported %v - missing types", x) return true } if (obj1Type.Kind == c2go.Uint32 || obj1Type.Kind == c2go.Int32) && obj2Type.Kind == c2go.Float32 { x.Op = cc.Eq x.Left = obj1 x.Right = &cc.Expr{ Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "math.Float32bits", }, List: []*cc.Expr{obj2}, } x.XType = uint32Type return true } fprintf(x.Span, "unsupported %v - size 4 type %v %v", x, c2go.GoString(obj1Type), c2go.GoString(obj2Type)) } if siz.Op == cc.Number && siz.Text == "8" { obj1, obj1Type := objIndir(fn, x.List[0]) obj2, obj2Type := objIndir(fn, x.List[1]) if obj1Type == nil || obj2Type == nil { fprintf(x.Span, "unsupported %v - missing types", x) return true } if (obj1Type.Kind == c2go.Uint64 || obj1Type.Kind == c2go.Int64) && obj2Type.Kind == c2go.Float64 { x.Op = cc.Eq x.Left = obj1 x.Right = &cc.Expr{ Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "math.Float64bits", }, List: []*cc.Expr{obj2}, } x.XType = uint64Type return true } fprintf(x.Span, "unsupported %v - size 8 type %v %v", x, c2go.GoString(obj1Type), c2go.GoString(obj2Type)) } if siz.Op == cc.SizeofExpr { obj1Type := fixGoTypesExpr(fn, x.List[0], nil) obj2Type := fixGoTypesExpr(fn, x.List[1], nil) sizeType := fixGoTypesExpr(fn, siz.Left, nil) if obj1Type == nil || obj2Type == nil { fprintf(x.Span, "unsupported %v - bad types", x) return true } if obj2Type.Kind == cc.Array && sameType(obj2Type, sizeType) || obj2Type.Kind == c2go.Slice && c2go.GoString(x.List[1]) == c2go.GoString(siz.Left) { x.Left.Text = "copy" x.Left.XDecl = nil x.List = x.List[:2] return true } fprintf(x.Span, "unsupported %v - not array %v %v", x, c2go.GoString(obj2Type), c2go.GoString(sizeType)) return true } left := fixGoTypesExpr(fn, x.List[0], nil) right := fixGoTypesExpr(fn, x.List[1], nil) fixGoTypesExpr(fn, siz, nil) if isSliceOrArray(left) && isSliceOrArray(right) && left.Base.Is(c2go.Uint8) && right.Base.Is(c2go.Uint8) { x.Left.Text = "copy" x.Left.XDecl = nil if x.List[1].Op == c2go.ExprSlice && x.List[1].List[1] == nil { x.List[1].List[2] = siz } else { x.List[1] = &cc.Expr{Op: c2go.ExprSlice, List: []*cc.Expr{x.List[1], nil, siz}} } x.List = x.List[:2] return true } fprintf(x.Span, "unsupported %v (%v %v)", x, c2go.GoString(left), c2go.GoString(right)) return true case "mal", "malloc", "emallocz": if len(x.List) != 1 { fprintf(x.Span, "unsupported %v - too many args", x) return false } siz := x.List[0] var count *cc.Expr if siz.Op == cc.Mul { count = siz.Left siz = siz.Right } var typ *cc.Type switch siz.Op { default: typ = byteType count = siz case cc.SizeofExpr: typ = fixGoTypesExpr(fn, siz.Left, nil) if typ == nil { fprintf(siz.Span, "failed to type check %v", siz.Left) } case cc.SizeofType: typ = siz.Type if typ == nil { fprintf(siz.Span, "sizeoftype missing type") } } if typ == nil { fprintf(x.Span, "unsupported %v - cannot understand type", x) return true } if count == nil { x.Left.Text = "new" x.Left.XDecl = nil x.List = []*cc.Expr{&cc.Expr{Op: cc.Name, Text: c2go.GoString(typ)}} x.XType = &cc.Type{Kind: cc.Ptr, Base: typ} } else { x.Left.Text = "make" x.Left.XDecl = nil x.List = []*cc.Expr{ &cc.Expr{Op: cc.Name, Text: "[]" + c2go.GoString(typ)}, count, } x.XType = &cc.Type{Kind: c2go.Slice, Base: typ} } return true case "strdup", "estrdup": if len(x.List) != 1 { fprintf(x.Span, "unsupported %v - too many args", x) return false } fixGoTypesExpr(fn, x.List[0], stringType) fixMerge(x, x.List[0]) x.XType = stringType return true case "strcpy", "strcat", "fmtstrcpy": if len(x.List) != 2 { fprintf(x.Span, "unsupported %v - too many args", x) return false } fixGoTypesExpr(fn, x.List[0], nil) fixGoTypesExpr(fn, x.List[1], stringType) x.Op = cc.Eq if x.Left.Text == "strcat" || x.Left.Text == "fmtstrcpy" { x.Op = cc.AddEq } x.Left = x.List[0] x.Right = x.List[1] x.XType = stringType return true case "strlen": x.Left.Text = "len" x.Left.XDecl = nil x.XType = intType return true case "TUP", "CASE": if len(x.List) != 2 { fprintf(x.Span, "unsupported %v - too many args", x) return false } left := fixGoTypesExpr(fn, x.List[0], targ) right := fixGoTypesExpr(fn, x.List[1], targ) x.Op = cc.Or x.Left = &cc.Expr{Op: cc.Lsh, Left: x.List[0], Right: &cc.Expr{Op: cc.Number, Text: "16"}, XType: left} x.Right = x.List[1] x.List = nil x.XType = fixBinary(fn, x, left, right, targ) return true } return false }
func fixPrintf(curfn *cc.Decl, x *cc.Expr) bool { if x.Op != cc.Call { return false } if tryPrintf(curfn, x, "sprint", 1, "fmt.Sprintf") { targ := x.List[0] x.List = x.List[1:] x.Right = copyExpr(x) x.List = nil x.Left = targ x.Op = cc.Eq // sprint(strchr(s, 0), "hello") => s += "hello" if targ.Op == cc.Call && len(targ.List) == 2 && targ.Left.Text == "strchr" && targ.List[1].Text == "0" { x.Left = targ.List[0] x.Op = cc.AddEq } else if targ.Op == cc.Add && targ.Right.Op == cc.Call && targ.Right.Left.Text == "strlen" && len(targ.Right.List) == 1 && c2go.GoString(targ.Left) == c2go.GoString(targ.Right.List[0]) { x.Left = targ.Left x.Op = cc.AddEq } return true } if tryPrintf(curfn, x, "snprint", 2, "fmt.Sprintf") { targ := x.List[0] x.List = x.List[2:] x.Right = copyExpr(x) x.List = nil x.Left = targ x.Op = cc.Eq return true } if tryPrintf(curfn, x, "fmtprint", 1, "fmt.Sprintf") { targ := x.List[0] x.List = x.List[1:] x.Right = copyExpr(x) x.List = nil x.Left = targ x.Op = cc.AddEq return true } if tryPrintf(curfn, x, "print", 0, "fmt.Printf") { return true } if tryPrintf(curfn, x, "sysfatal", 0, "log.Fatalf") { return true } if tryPrintf(curfn, x, "ctxt.diag", 0, "ctxt.diag") { return true } if tryPrintf(curfn, x, "Bprint", 1, "fmt.Fprintf") { return true } if tryPrintf(curfn, x, "smprint", 0, "fmt.Sprintf") { return true } return false }