func zeroFor(targ *cc.Type) *cc.Expr { if targ != nil { k := targ.Def().Kind switch k { case String: return &cc.Expr{Op: cc.String, Texts: []string{`""`}} case Slice, cc.Ptr: return &cc.Expr{Op: cc.Name, Text: "nil"} case cc.Struct, cc.Array: return &cc.Expr{Op: cc.CastInit, Type: targ, Init: &cc.Init{}} case Bool: return &cc.Expr{Op: cc.Name, Text: "false"} } if Int8 <= k && k <= Float64 { return &cc.Expr{Op: cc.Number, Text: "0"} } if isEmptyInterface(targ) { return &cc.Expr{Op: cc.Name, Text: "nil"} } return &cc.Expr{Op: cc.Number, Text: "0 /*" + targ.String() + "*/"} } return &cc.Expr{Op: cc.Number, Text: "0 /*untyped*/"} }
func sameType(t, u *cc.Type) bool { t = t.Def() u = u.Def() if t == u { return true } if t == nil || u == nil { return false } if t.Kind != u.Kind { return false } if t.Name != "" || u.Name != "" { return t.Name == u.Name } if !sameType(t.Base, u.Base) || len(t.Decls) != len(u.Decls) { return false } for i, td := range t.Decls { ud := u.Decls[i] if !sameType(td.Type, ud.Type) || t.Kind == cc.Struct && td.Name != ud.Name { return false } } return true }
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 == Uint32 || obj1Type.Kind == Int32) && obj2Type.Kind == 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, GoString(obj1Type), 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 == Uint64 || obj1Type.Kind == Int64) && obj2Type.Kind == 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, GoString(obj1Type), 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 == Slice && GoString(x.List[1]) == 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, GoString(obj2Type), 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(Uint8) && right.Base.Is(Uint8) { x.Left.Text = "copy" x.Left.XDecl = nil if x.List[1].Op == ExprSlice && x.List[1].List[1] == nil { x.List[1].List[2] = siz } else { x.List[1] = &cc.Expr{Op: ExprSlice, List: []*cc.Expr{x.List[1], nil, siz}} } x.List = x.List[:2] return true } // fprintf(x.Span, "unsupported %v (%v %v)", x, GoString(left), GoString(right)) return true case "mal", "malloc", "emallocz", "xmalloc": 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 if count.Op == cc.SizeofType || count.Op == cc.SizeofExpr { count, siz = siz, count } } 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: ExprType, Type: typ}} x.XType = &cc.Type{Kind: cc.Ptr, Base: typ} if typ.String() == "Prog" { isGC := strings.Contains(x.Span.Start.File, "cmd/gc") isCompiler := isGC || strings.Contains(x.Span.Start.File, "cmd/6g") || strings.Contains(x.Span.Start.File, "cmd/8g") || strings.Contains(x.Span.Start.File, "cmd/5g") || strings.Contains(x.Span.Start.File, "cmd/9g") if isCompiler { x.List = nil x.Left.Text = "Ctxt.NewProg" if !isGC { x.Left.Text = "gc." + x.Left.Text } } } } else { x.Left.Text = "make" x.Left.XDecl = nil x.XType = &cc.Type{Kind: Slice, Base: typ} x.List = []*cc.Expr{ &cc.Expr{Op: ExprType, Type: x.XType}, count, } } 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 "strcmp": if len(x.List) != 2 { fprintf(x.Span, "unsupported %v - too many args", x) return false } fixGoTypesExpr(fn, x.List[0], stringType) fixGoTypesExpr(fn, x.List[1], stringType) x.Left.Text = "stringsCompare" x.Left.XDecl = nil x.XType = intType return true case "abort": x.Left.Text = "panic" x.Left.XDecl = nil x.List = []*cc.Expr{{Op: cc.Name, Text: `"abort"`}} 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) forceConvert(fn, x.List[0], left, uint32Type) forceConvert(fn, x.List[1], right, uint32Type) 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 = uint32Type return true case "R": 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) forceConvert(fn, x.List[0], left, uint32Type) forceConvert(fn, x.List[1], right, uint32Type) x.Op = cc.Or x.Left = x.List[0] x.Right = &cc.Expr{Op: cc.Lsh, Left: x.List[1], Right: &cc.Expr{Op: cc.Number, Text: "24"}, XType: left} x.List = nil x.XType = uint32Type return true case "FCASE": if len(x.List) != 3 { fprintf(x.Span, "unsupported %v - too many args", x) return false } arg0 := fixGoTypesExpr(fn, x.List[0], targ) arg1 := fixGoTypesExpr(fn, x.List[1], targ) arg2 := fixGoTypesExpr(fn, x.List[2], targ) forceConvert(fn, x.List[0], arg0, uint32Type) forceConvert(fn, x.List[1], arg1, uint32Type) forceConvert(fn, x.List[2], arg2, uint32Type) x.Op = cc.Or x.Left = &cc.Expr{Op: cc.Lsh, Left: x.List[0], Right: &cc.Expr{Op: cc.Number, Text: "16"}, XType: uint32Type} x.Right = &cc.Expr{ Op: cc.Or, Left: &cc.Expr{Op: cc.Lsh, Left: x.List[1], Right: &cc.Expr{Op: cc.Number, Text: "8"}, XType: uint32Type}, Right: x.List[2], } x.List = nil x.XType = uint32Type return true } return false }
func toGoType(cfg *Config, x cc.Syntax, typ *cc.Type, cache map[*cc.Type]*cc.Type) *cc.Type { if typ == nil { return nil } if cache[typ] != nil { return cache[typ] } switch typ.Kind { default: panic(fmt.Sprintf("unexpected C type %s", typ)) case Ideal: return typ case cc.Void: return &cc.Type{Kind: cc.Struct} // struct{} case cc.Char, cc.Uchar, cc.Short, cc.Ushort, cc.Int, cc.Uint, cc.Long, cc.Ulong, cc.Longlong, cc.Ulonglong, cc.Float, cc.Double, cc.Enum: t := &cc.Type{Kind: c2goKind[typ.Kind]} if d, ok := x.(*cc.Decl); ok { if cfg.bool[declKey(d)] { t.Kind = Bool } else if strings.HasPrefix(d.Name, "no") { println("not bool", d.Name, declKey(d)) } } return t case cc.TypedefType: if cfg.typeMap[typ.Name] != "" { t := &cc.Type{Kind: cc.TypedefType, Name: cfg.typeMap[typ.Name], TypeDecl: typ.TypeDecl} cache[typ] = t return t } // If this is a typedef like uchar, translate the type by name. // Otherwise fall back to base. def := typ.Base if cc.Char <= def.Kind && def.Kind <= cc.Enum { var t *cc.Type if c2goName[typ.Name] != 0 { t = &cc.Type{Kind: c2goName[typ.Name]} } else { t = &cc.Type{Kind: c2goKind[typ.Base.Kind]} } if d, ok := x.(*cc.Decl); ok { if cfg.bool[declKey(d)] { t.Kind = Bool } else if strings.HasPrefix(d.Name, "no") { println("not bool", d.Name, declKey(d)) } } return t } if typ.Name == "va_list" { return &cc.Type{Kind: cc.TypedefType, Name: "[]interface{}"} } // Otherwise assume it is a struct or some such, // and preserve the name but translate the base. t := &cc.Type{Kind: cc.TypedefType, Name: typ.Name, TypeDecl: typ.TypeDecl} cache[typ] = t t.Base = toGoType(cfg, nil, typ.Base, cache) return t case cc.Array: if typ.Base.Def().Kind == cc.Char { return &cc.Type{Kind: String} } t := &cc.Type{Kind: cc.Array, Width: typ.Width} cache[typ] = t t.Base = toGoType(cfg, nil, typ.Base, cache) return t case cc.Ptr: t := &cc.Type{Kind: cc.Ptr} cache[typ] = t base := x if typ.Base.Kind != cc.Func { base = nil } t.Base = toGoType(cfg, base, typ.Base, cache) // Convert void* to interface{}. if typ.Base.Kind == cc.Void { t.Base = nil t.Kind = cc.TypedefType t.Name = "interface{}" return t } if typ.Base.Kind == cc.Char { t.Kind = String t.Base = nil return t } d, ok := x.(*cc.Decl) if typ.Base.Def().Kind == cc.Uchar && (!ok || !cfg.ptr[declKey(d)]) { t.Kind = Slice t.Base = byteType } if ok && cfg.slice[declKey(d)] { t.Kind = Slice } return t case cc.Func: // A func Type contains Decls, and we don't fork the Decls, so don't fork the Type. // The Decls themselves appear in the group lists, so they'll be handled by rewriteTypes. // The return value has no Decl and needs to be converted. if !typ.Base.Is(cc.Void) { if d, ok := x.(*cc.Decl); ok { x = &cc.Decl{ Name: "return", CurFn: d, } } else { x = nil } typ.Base = toGoType(cfg, x, typ.Base, cache) } // Check for array passed as parameter. Doesn't translate well. for _, d := range typ.Decls { if d.Type.Is(cc.Array) { fprintf(d.Span, "function taking array parameter!") } } return typ case cc.Struct: // A struct Type contains Decls, and we don't fork the Decls, so don't fork the Type. // The Decls themselves appear in the group lists, so they'll be handled by rewriteTypes. return typ } }
func (p *Printer) printType(t *cc.Type) { if t == nil { p.Print("nil_type") return } // Shouldn't happen but handle in case it does. p.Print(t.Comments.Before) defer p.Print(t.Comments.Suffix, t.Comments.After) if t == cc.BoolType { p.Print("bool") return } if typemap[t.Kind] != "" { p.Print(typemap[t.Kind]) return } switch t.Kind { default: if t.String() == "" { p.Print("C.unknown") break } p.Print("C.", t.String()) // hope for the best case Slice: p.Print("[]", t.Base) case String: p.Print("string") case cc.Struct: if len(t.Decls) == 0 { p.Print("struct{}") break } p.Print("struct {", Indent) p.printStructBody(t) p.Print(Unindent, Newline, "}") case cc.Enum: if t.Tag != "" { p.Print(t.Tag) } else { p.Print("int") } case cc.TypedefType: if t.Base != nil && typemap[t.Base.Kind] != "" && strings.ToLower(t.Name) == t.Name { p.Print(typemap[t.Base.Kind]) return } if t.TypeDecl != nil && t.TypeDecl.GoPackage != "" && p.Package != "" && t.TypeDecl.GoPackage != p.Package { p.Print(path.Base(t.TypeDecl.GoPackage) + "." + t.Name) break } p.Print(t.Name) case cc.Ptr: if t.Base.Is(cc.Func) { p.Print(t.Base) return } if t.Base.Is(cc.Void) { p.Print("*[0]byte") return } p.Print("*", t.Base) case cc.Func: p.Print("func(") for i, arg := range t.Decls { if i > 0 { p.Print(", ") } if arg.Name == "..." { p.Print("...interface{}") continue } if arg.Name == "" && arg.Type.Is(cc.Void) { continue } p.Print(arg.Type) } p.Print(")") if !t.Base.Is(cc.Void) { p.Print(" ", t.Base) } case cc.Array: if t.Width == nil { p.Print("[XXX]", t.Base) return } p.Print("[", t.Width, "]", t.Base) } }
func (p *Printer) printInit(typ *cc.Type, x *cc.Init) { p.Print(x.Comments.Before) defer p.Print(x.Comments.Suffix, x.Comments.After) if len(x.Prefix) > 0 { for _, pre := range x.Prefix { p.Print(pre) } } if x.Expr != nil { if x.Expr.Op == cc.Number && (typ.Is(cc.Ptr) || typ.Is(Slice)) { p.Print("nil") return } p.printExpr(x.Expr, precComma) return } nl := len(x.Braced) > 0 && x.Braced[0].Span.Start.Line != x.Braced[len(x.Braced)-1].Span.End.Line if typ != nil { p.printType(typ) } p.Print("{") if nl { p.Print(Indent) } warned := false for i, y := range x.Braced { if nl { p.Print(Newline) } else if i > 0 { p.Print(" ") } var subtyp *cc.Type if typ != nil { if typ.Is(cc.Struct) && i < len(typ.Def().Decls) && len(y.Prefix) == 0 { subtyp = typ.Def().Decls[i].Type } else if typ.Is(cc.Struct) && len(y.Prefix) == 1 && y.Prefix[0].XDecl != nil { subtyp = y.Prefix[0].XDecl.Type } else if typ.Is(cc.Array) || typ.Is(Slice) { subtyp = typ.Def().Base } else if !warned { warned = true fprintf(x.Span, "too many fields in braced initializer of %s", GoString(typ)) } } p.printInit(subtyp, y) p.Print(",") } if typ != nil && typ.Is(cc.Struct) && len(x.Braced) > 0 && len(x.Braced[0].Prefix) == 0 && len(x.Braced) < len(typ.Def().Decls) { for i := len(x.Braced); i < len(typ.Def().Decls); i++ { subtyp := typ.Def().Decls[i].Type if subtyp.Is(cc.Ptr) || subtyp.Is(Slice) { p.Print(" nil,") } else if subtyp.Is(cc.Array) { p.Print(" ", subtyp, "{},") } else { p.Print(" 0,") } } } if nl { p.Print(Unindent, Newline) } p.Print("}") }
func cTypeToMap(typ *cc.Type) *typeMap { switch typ.Kind { case cc.Ptr: str := typ.Base.String() switch str { case "char": return &typeMap{ goType: "string", cToGo: func(in string) string { return fmt.Sprintf("C.GoString(%s)", in) }, goToC: func(in string) (string, string) { cvar := fmt.Sprintf("c_%s", in) return cvar, fmt.Sprintf("%s := C.CString(%s); defer C.free(unsafe.Pointer(%s))", cvar, in, cvar) }, } case "uchar", "void": log.Printf("TODO %s: in type blacklist (TODO: add reasoning)", str) return nil } if goType, ok := sharedTypes[str]; ok { // TODO: it appears *Rectangle might only be used for out params. return &typeMap{ goType: "*" + goType, cToGo: func(in string) string { return fmt.Sprintf("(*%s)(unsafe.Pointer(%s))", goType, in) }, goToC: func(in string) (string, string) { return fmt.Sprintf("(*C.%s)(unsafe.Pointer(%s))", str, in), "" }, method: goType, } } if rawCTypes[str] { return &typeMap{ goType: "unsafe.Pointer", cToGo: func(in string) string { return fmt.Sprintf("unsafe.Pointer(%s)", in) }, goToC: func(in string) (string, string) { return fmt.Sprintf("(*C.%s)(%s)", str, in), "" }, } } goName := cNameToGoUpper(str) if reason, ok := typeTodoList[str]; ok { log.Printf("TODO %s: %s", str, reason) return nil } return &typeMap{ goType: "*" + goName, cToGo: func(in string) string { return fmt.Sprintf("wrap%s(%s)", goName, in) }, goToC: func(in string) (string, string) { return fmt.Sprintf("%s.Ptr", in), "" }, method: goName, } case cc.Void: return &typeMap{ goType: "", cToGo: nil, goToC: nil, } } // Otherwise, it's a basic non-pointer type. cName := typ.String() if reason, ok := typeTodoList[cName]; ok { log.Printf("TODO %s: %s", cName, reason) return nil } switch cName { case "cairo_bool_t": return &typeMap{ goType: "bool", cToGo: func(in string) string { return fmt.Sprintf("%s != 0", in) }, goToC: func(in string) (string, string) { return fmt.Sprintf("C.%s(%s)", cName, in), "" }, } case "cairo_status_t": return &typeMap{ goType: "error", cToGo: func(in string) string { return fmt.Sprintf("Status(%s).toError()", in) }, goToC: nil, } case "Drawable", "Pixmap": return &typeMap{ goType: "uint64", cToGo: func(in string) string { return fmt.Sprintf("uint64(%s)", in) }, goToC: func(in string) (string, string) { return fmt.Sprintf("C.%s(%s)", cName, in), "" }, } } goName := cNameToGoUpper(cName) m := &typeMap{ goType: goName, cToGo: func(in string) string { return fmt.Sprintf("%s(%s)", goName, in) }, goToC: func(in string) (string, string) { return fmt.Sprintf("C.%s(%s)", cName, in), "" }, } if goName == "Format" { // Attempt to put methods on our "Format" type. m.method = goName } return m }