func exportDecls(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 } } }) } for _, d := range cfg.topDecls { renameDecl(cfg, 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 fixQsortCmp(decl *cc.Decl) (*cc.Type, *cc.Decl) { ftyp := decl.Type if ftyp.Kind != cc.Func || len(ftyp.Decls) != 2 || !isEmptyInterface(ftyp.Decls[0].Type) || !isEmptyInterface(ftyp.Decls[1].Type) { fprintf(decl.Span, "invalid qsort cmp function %v - wrong args", GoString(ftyp)) return nil, 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 == TypeAssert || 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 indir2 = r != x.Right } } } }) if p1 == nil || p2 == nil { fprintf(decl.Span, "invalid qsort cmp function - cannot find arg extraction") return nil, nil } if !sameType(p1.XType, p2.XType) { fprintf(decl.Span, "invalid qsort cmp function - different arg types %v and %v", GoString(p1.XType), GoString(p2.XType)) return nil, nil } if indir1 != indir2 { fprintf(decl.Span, "invalid qsort cmp function - different arg indirection") return nil, 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", GoString(typ)) return nil, 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. newDecl := *decl decl.Body = nil decl = &newDecl 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.Left.Text == "1" { if ret.Op == cc.Plus { ret.Text = "false" } else { ret.Text = "true" } ret.Op = cc.Name ret.Left = nil ret.XType = boolType return } case cc.Number: if ret.Text == "0" { 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, decl }
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 } }) fp := fn.Type.Decls[0] fp.Type = stringType fn.Type.Base = stringType if arg != nil { fn.Type.Decls[0] = arg.XDecl } else { if len(fn.Type.Decls) == 1 { fprintf(fn.Span, "missing va_arg in formatter") return } fn.Type.Decls = fn.Type.Decls[1:] decl := &cc.Stmt{ Op: cc.StmtDecl, Decl: fp, } fn.Body.Block = append([]*cc.Stmt{decl}, fn.Body.Block...) } 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, } } if len(fn.Name) == 5 && strings.HasSuffix(fn.Name, "conv") { switch fn.Name[0] { case 'B', 'E', 'F', 'H', 'J', 'N', 'O', 'Q', 'S', 'T', 'V', 'Z': fn.Type.Decls = append(fn.Type.Decls, &cc.Decl{ Name: "flag", Type: intType, }) } } cc.Preorder(fn.Body, func(x cc.Syntax) { switch x := x.(type) { case *cc.Stmt: if arg != nil && 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 } } }) }
func fixPrintFormat(curfn *cc.Decl, fx *cc.Expr, args []*cc.Expr) []*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 } }) } isGC := strings.Contains(fx.Span.Start.File, "cmd/gc") isCompiler := isGC || strings.Contains(fx.Span.Start.File, "cmd/6g") || strings.Contains(fx.Span.Start.File, "cmd/8g") || strings.Contains(fx.Span.Start.File, "cmd/5g") || strings.Contains(fx.Span.Start.File, "cmd/9g") narg := 0 for j, text := range fx.Texts { format, err := strconv.Unquote(text) if err != nil { fprintf(fx.Span, "cannot parse quoted string: %v", err) return args } suffix := "" var buf bytes.Buffer start := 0 for i := 0; i < len(format); i++ { if format[i] != '%' { continue } buf.WriteString(format[start:i]) start = i i++ if i < len(format) && format[i] == '%' { buf.WriteByte('%') buf.WriteByte('%') start = i + 1 continue } 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 args } 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:] } narg += strings.Count(allFlags, "*") 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', '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 Int8: convert = "uint8" case Int16: convert = "uint16" case Int32: convert = "uint32" case Int64: convert = "uint64" case Int: convert = "uint" } } case 'C': // rune buf.WriteString(flags) buf.WriteString("c") case 'q': // plan 9 rc(1) quoted string buf.WriteString(flags) buf.WriteString("v") convert = "plan9quote" case 'A': // asm opcode if allFlags != "%" { fprintf(fx.Span, "format %s%c", allFlags, verb) } buf.WriteString("%v") if narg < len(args) { forceConvert(nil, args[narg], args[narg].XType, intType) } convert = "Aconv" + suffix if isCompiler { switch { case strings.Contains(fx.Span.Start.File, "cmd/6g"): convert = "amd64." + convert case strings.Contains(fx.Span.Start.File, "cmd/8g"): convert = "i386." + convert case strings.Contains(fx.Span.Start.File, "cmd/5g"): convert = "arm." + convert case strings.Contains(fx.Span.Start.File, "cmd/9g"): convert = "ppc64." + convert } } 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" if isCompiler { if isGC { convert = "Ctxt.Line" } else { convert = "gc.Ctxt.Line" } forceConvert(curfn, arg, arg.XType, intType) } case '@': if allFlags != "%" { fprintf(fx.Span, "format %s%c", allFlags, verb) } buf.WriteString("%v") convert = "RAconv" + suffix case '^': if allFlags != "%" { fprintf(fx.Span, "format %s%c", allFlags, verb) } buf.WriteString("%v") convert = "DRconv" + 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 = "obj.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, } if isCompiler { args[narg].List = args[narg].List[2:] args[narg].Left.Text = "Ctxt.Dconv" if !isGC { args[narg].Left.Text = "gc.Ctxt.Dconv" } } 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") forceConvert(curfn, args[narg], args[narg].XType, intType) convert = "Rconv" + suffix if isCompiler { convert = "Ctxt." + convert if !isGC { convert = "gc." + convert } } 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") case 'r': // plan 9 errstr buf.WriteString("%v") if narg > len(args) { break } args = append(append(args[:narg:narg], &cc.Expr{Op: cc.Name, Text: "err"}), args[narg:]...) case 'B', 'E', 'F', 'H', 'J', 'N', 'O', 'Q', 'S', 'T', 'V', 'Z': switch verb { case 'E', 'O': convert = "int" } f := allFlags mod := "0" if strings.Contains(f, "-") { mod += "|obj.FmtLeft" f = strings.Replace(f, "-", "", 1) } if strings.Contains(f, "h") { mod += "|obj.FmtShort" f = strings.Replace(f, "h", "", 1) if strings.Contains(f, "h") { mod += "|obj.FmtByte" f = strings.Replace(f, "h", "", 1) } } if strings.Contains(f, "#") { mod += "|obj.FmtSharp" f = strings.Replace(f, "#", "", 1) } if strings.Contains(f, "l") { mod += "|obj.FmtLong" f = strings.Replace(f, "l", "", 1) } if strings.Contains(f, ",") { mod += "|obj.FmtComma" f = strings.Replace(f, ",", "", 1) } if strings.Contains(f, "+") { mod += "|obj.FmtSign" f = strings.Replace(f, "+", "", 1) } if strings.Contains(f, "u") { mod += "|obj.FmtUnsigned" f = strings.Replace(f, "u", "", 1) } if f != "%" { fprintf(fx.Span, "format %s%c", allFlags, verb) } buf.WriteString("%v") if narg >= len(args) { break } if mod != "0" { mod = mod[2:] } if !isCompiler { mod = strings.Replace(mod, "obj.", "", -1) } flag := &cc.Expr{Op: cc.Name, Text: mod} if convert != "" { args[narg] = &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: convert}, List: []*cc.Expr{args[narg]}, XType: stringType} convert = "" } arg := args[narg] args[narg] = &cc.Expr{ Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: string(verb) + "conv"}, List: []*cc.Expr{ arg, flag, }, XType: stringType, } if !isGC { args[narg].Left.Text = "gc." + args[narg].Left.Text } } 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} } narg++ } buf.WriteString(format[start:]) fx.Texts[j] = strconv.Quote(buf.String()) } return args }
// Apply DeMorgan's law and invert comparisons // to simplify negation of boolean expressions. func simplifyBool(cfg *Config, prog *cc.Prog) { cc.Preorder(prog, func(x cc.Syntax) { switch x := x.(type) { case *cc.Expr: switch x.Op { case cc.Not: y := x.Left for y.Op == cc.Paren { y = y.Left } switch y.Op { case cc.AndAnd: *x = *y x.Left = &cc.Expr{Op: cc.Not, Left: x.Left} x.Right = &cc.Expr{Op: cc.Not, Left: x.Right} x.Op = cc.OrOr case cc.OrOr: *x = *y x.Left = &cc.Expr{Op: cc.Not, Left: x.Left} x.Right = &cc.Expr{Op: cc.Not, Left: x.Right} x.Op = cc.AndAnd case cc.EqEq: if isfloat(x.Left.XType) { break } *x = *y x.Op = cc.NotEq case cc.NotEq: if isfloat(x.Left.XType) { break } *x = *y x.Op = cc.EqEq case cc.Lt: if isfloat(x.Left.XType) { break } *x = *y x.Op = cc.GtEq case cc.LtEq: if isfloat(x.Left.XType) { break } *x = *y x.Op = cc.Gt case cc.Gt: if isfloat(x.Left.XType) { break } *x = *y x.Op = cc.LtEq case cc.GtEq: if isfloat(x.Left.XType) { break } *x = *y x.Op = cc.Lt } } } }) }
// Rewrite from C constructs to Go constructs. func rewriteSyntax(cfg *Config, prog *cc.Prog) { numRewrite++ cc.Preorder(prog, func(x cc.Syntax) { switch x := x.(type) { case *cc.Stmt: rewriteStmt(x) case *cc.Expr: switch x.Op { case cc.Name: switch x.Text { case "nil": x.XDecl = nil // just nil, not main.Nil case "nelem": x.Text = "len" x.XDecl = nil } 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 == 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 } } }) }
// 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 goKeyword[x.Name] { // NOTE: Must put _ last so that name can be upper-cased for export. x.Name += "_" } case *cc.Stmt: for _, lab := range x.Labels { if goKeyword[lab.Name] { lab.Name += "_" } } switch x.Op { case cc.Goto: if goKeyword[x.Text] { x.Text += "_" } } } }) // Assign to packages (needed below but also in writeGoFiles). for _, d := range prog.Decls { if d.Body != nil && d.Body.Span.Start.File != "" { d.Span = d.Body.Span } d.GoPackage = cfg.filePackage(d.Span.Start.File) } // 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 { // TODO(rsc): I don't understand why this is necessary given the above. if d.Body != nil && d.Body.Span.Start.File != "" { d.Span = d.Body.Span } d.GoPackage = cfg.filePackage(d.Span.Start.File) key := d.GoPackage + "." + d.Name if count[key]++; count[key] > 1 { if d.Span.String() == src[key] { // Assume this is a nested header and ignore duplicates. count[key] = 1 continue } fprintf(d.Span, "conflicting name %s in %s (last at %s)", d.Name, d.GoPackage, 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 } if d.Type.Kind == cc.Func { if d.Body != nil { for _, s := range d.Body.Block { if s.Op == cc.StmtDecl && s.Decl.Storage&cc.Static != 0 { // Add function name as prefix. // Will print at top level. dd := s.Decl dd.Name = d.Name + "_" + dd.Name } } } } } cfg.topDecls = decls }