Example #1
0
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*/"}
}
Example #2
0
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
}
Example #3
0
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
}
Example #4
0
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
	}
}
Example #5
0
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)
	}
}
Example #6
0
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("}")
}
Example #7
0
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
}