Пример #1
0
func negate(x *cc.Expr) *cc.Expr {
	switch x.Op {
	case cc.Paren:
		return negate(x.Left)
	case cc.OrOr:
		x.Op = cc.AndAnd
		x.Left = negate(x.Left)
		x.Right = negate(x.Right)
	case cc.AndAnd:
		x.Op = cc.OrOr
		x.Left = negate(x.Left)
		x.Right = negate(x.Right)
	case cc.EqEq:
		x.Op = cc.NotEq
	case cc.NotEq:
		x.Op = cc.EqEq
	case cc.Lt:
		x.Op = cc.GtEq
	case cc.GtEq:
		x.Op = cc.Lt
	case cc.Gt:
		x.Op = cc.LtEq
	case cc.LtEq:
		x.Op = cc.Gt
	default:
		x = &cc.Expr{Op: cc.Not, Left: x}
	}
	return x
}
Пример #2
0
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 && GoString(targ.Left) == 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
		if x.Left.Op == cc.Addr {
			x.Left = x.Left.Left
		}
		return true
	}
	if tryPrintf(curfn, x, "smprint", 0, "fmt.Sprintf") {
		x.XType = stringType
		return true
	}
	if tryPrintf(curfn, x, "print", 0, "fmt.Printf") {
		return true
	}
	if tryPrintf(curfn, x, "fprint", 1, "fmt.Fprintf") {
		if x.List[0].String() == "2" {
			x.List[0] = &cc.Expr{Op: cc.Name, Text: "os.Stderr"}
		}
		return true
	}
	if tryPrintf(curfn, x, "sysfatal", 0, "log.Fatalf") {
		return true
	}
	if tryPrintf(curfn, x, "fatal", 0, "") {
		return true
	}
	if tryPrintf(curfn, x, "ctxt->diag", 0, "") {
		return true
	}
	if tryPrintf(curfn, x, "diag", 0, "") {
		return true
	}
	if tryPrintf(curfn, x, "werrstr", 0, "") {
		return true
	}
	if tryPrintf(curfn, x, "yyerror", 0, "") {
		return true
	}
	if tryPrintf(curfn, x, "yyerrorl", 1, "") {
		forceConvert(curfn, x.List[0], x.List[0].XType, intType)
		return true
	}
	if tryPrintf(curfn, x, "onearg", 1, "") {
		return true
	}
	if tryPrintf(curfn, x, "warn", 0, "") {
		return true
	}
	if tryPrintf(curfn, x, "warnl", 1, "") {
		forceConvert(curfn, x.List[0], x.List[0].XType, intType)
		return true
	}
	if tryPrintf(curfn, x, "Bprint", 1, "fmt.Fprintf") {
		return true
	}

	if isCall(x, "exprfmt") && len(x.List) == 3 {
		// exprfmt(fp, x, y) becomes fp += exprfmt(x, y)
		x.Op = cc.AddEq
		x.Right = &cc.Expr{Op: cc.Call, Left: x.Left, List: x.List[1:]}
		x.Left = x.List[0]
		x.List = nil
		x.XType = stringType
		return true
	}

	if isCall(x, "fmtstrinit") && len(x.List) == 1 && x.List[0].Op == cc.Addr {
		x.Op = cc.Eq
		x.Left = x.List[0].Left
		x.List = nil
		x.Right = &cc.Expr{Op: cc.Name, Text: `""`}
	}

	if isCall(x, "fmtstrflush") && len(x.List) == 1 && x.List[0].Op == cc.Addr {
		x.Op = cc.Paren
		x.Left = x.List[0].Left
		x.List = nil
	}

	return false
}
Пример #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
}
Пример #4
0
func forceConvert(fn *cc.Decl, x *cc.Expr, actual, targ *cc.Type) {
	if isEmptyInterface(targ) {
		return
	}
	if isEmptyInterface(actual) {
		old := copyExpr(x)
		x.Op = TypeAssert
		x.Left = old
		x.Right = nil
		x.List = nil
		x.Type = targ
		x.XType = targ
		return
	}

	if isNumericConst(x) && targ != nil {
		switch targ.Kind {
		case cc.Ptr, Slice:
			if x.Op == cc.Number && x.Text == "0" {
				x.Op = cc.Name
				x.Text = "nil"
				x.XType = targ
			}
		case String:
			if x.Op == cc.Number && x.Text == "0" {
				x.Op = cc.Name
				x.Text = `""`
				x.XType = targ
			}
		}
		return
	}

	if x.Op == cc.Name && x.Text == "nil" && targ != nil {
		switch targ.Kind {
		case cc.Func, cc.Ptr, Slice:
			return
		case String:
			x.Text = `""`
			x.XType = targ
			x.XDecl = nil
			return
		}
	}

	if actual == nil || targ == nil {
		return
	}

	if actual.Kind == Ideal && Int8 <= targ.Kind && targ.Kind <= Float64 {
		return
	}

	if x != nil && x.Op == cc.Name && x.Text == "nil" {
		if targ.Kind == cc.Func || targ.Kind == cc.Ptr || targ.Kind == Slice {
			return
		}
	}

	// Func conversions are never useful.
	// If the func types are different, the conversion will fail;
	// if not, the conversion is unnecessary.
	// Either way the conversion is an eyesore.
	if targ.Kind == cc.Func || targ.Kind == cc.Ptr && targ.Base.Kind == cc.Func {
		return
	}

	if actual.Kind == Bool && Int8 <= targ.Kind && targ.Kind <= Float64 {
		old := copyExpr(x)
		if targ.Kind == 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
		}
		return
	}

	if actual.Kind == cc.Array && targ.Kind == Slice && sameType(actual.Base, targ.Base) {
		old := copyExpr(x)
		x.Op = ExprSlice
		x.List = []*cc.Expr{old, nil, nil}
		x.Left = nil
		x.Right = nil
		return
	}

	if actual.Kind == Slice && targ.Kind == cc.Ptr && sameType(actual.Base, targ.Base) {
		if isCall(x, "make") {
			return
		}
		old := copyExpr(x)
		x.Op = cc.Addr
		x.Left = &cc.Expr{Op: cc.Index, Left: old, Right: &cc.Expr{Op: cc.Number, Text: "0"}}
		return
	}

	if !sameType(actual, targ) {
		if x.Op == cc.Twid {
			forceConvert(fn, x.Left, actual, targ)
			x.XType = targ
			return
		}
		old := copyExpr(x)
		// for debugging:
		// old = &cc.Expr{Op: cc.Cast, Left: old, Type: actual, XType: actual}
		x.Op = cc.Cast
		x.Left = old
		x.Right = nil
		x.List = nil
		x.Type = targ
		x.XType = targ
		if actual.Kind == cc.Array && targ.Kind == Slice {
			x.Op = ExprSlice
			x.List = []*cc.Expr{old, nil, nil}
			x.Left = nil
			x.Type = nil
		}
	}
}
Пример #5
0
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 != Bool {
			old := copyExpr(x)
			if targ.Kind == 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 == Bool {
			old := copyExpr(x)
			left := fixGoTypesExpr(fn, old, nil)
			if left != nil && left.Kind == 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)
			fixSpecialCompare(fn, x)
			return targ
		}
	}

	fixArray(fn, x)

	switch x.Op {
	default:
		panic(fmt.Sprintf("unexpected construct %v in fixGoTypesExpr - %v - %v", GoString(x), x.Op, x.Span))

	case ExprType:
		// inserted by a rewrite
		return nil

	case 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 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 == 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 = AndNot
			x.Right = x.Right.Left
		}

		if x.Op == cc.Add && isSliceStringOrArray(left) {
			fixGoTypesExpr(fn, x.Right, nil)
			x.Op = 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: 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 = 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: 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) && GoString(x.Left.Right) == "0" && 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)

		if x.Op == cc.Eq && x.Left != nil && x.Right != nil && x.Right.XType != nil && isCall(x.Right, "make") && x.Left.XDecl != nil && x.Left.XDecl.Type != nil && x.Left.XDecl.Type.Kind == cc.Ptr && sameType(x.Left.XDecl.Type.Base, x.Right.XType.Base) {
			x.Left.XDecl.Type = x.Right.XType
			x.Left.XType = x.Right.XType
			left = x.Right.XType
		}

		return left

	case 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 == Slice && sameType(targ.Base, left) {
			l := x.Left
			l.Op = 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)
		if left != nil && left.Kind == cc.Ptr && left.Base.Kind == cc.Func {
			left = left.Base
		}
		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)
		if isEmptyInterface(x.Left.XType) {
			x.Op = TypeAssert
		}
		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" || x.Right.Op == cc.Name && x.Right.Text == "nil" {
			if isSliceOrPtr(left) {
				x.Right.Op = cc.Name
				x.Right.Text = "nil"
				return boolType
			}
			if left != nil && left.Kind == 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, Slice, cc.Array:
			if x.Op == cc.Indir && left.Kind == cc.Ptr && left.Base.Kind == cc.Func {
				*x = *x.Left
			}
			return left.Base

		case String:
			return byteType
		}
		return nil

	case cc.Lsh, cc.Rsh:
		left := fixGoTypesExpr(fn, x.Left, targ)
		if left != nil && targ != nil && Int8 <= left.Kind && left.Kind <= 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", "C":
			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 {
			if x.Text == "true" || x.Text == "false" {
				return x.XType
			}
			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: 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 == Slice) && left.Base.Def().Is(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: String}

	case cc.VaArg:
		// TODO
		return nil
	}
}
Пример #6
0
func fixSpecialCompare(fn *cc.Decl, x *cc.Expr) bool {
	if (x.Right.Op != cc.Number || x.Right.Text != "0") && x.Right.String() != "nil" && x.Right.String() != `""` || x.Left.Op != cc.Call || x.Left.Left.Op != cc.Name {
		return false
	}

	call := x.Left
	switch call.Left.Text {
	case "memcmp":
		if len(call.List) != 3 {
			// fprintf(x.Span, "unsupported %v", x)
			return false
		}
		obj1, obj1Type := objIndir(fn, call.List[0])
		obj2, obj2Type := objIndir(fn, call.List[1])
		if obj1Type == nil || !sameType(obj1Type, obj2Type) {
			// fprintf(x.Span, "unsupported %v", call)
			return true
		}

		if !matchSize(fn, obj1, obj1Type, call.List[2]) && !matchSize(fn, obj2, obj2Type, call.List[2]) {
			// fprintf(x.Span, "unsupported %v - wrong size", call)
			return true
		}

		x.Left = obj1
		x.Right = obj2
		x.List = nil
		x.XType = boolType
		return true

	case "strncmp":
		if len(call.List) != 3 {
			fprintf(x.Span, "unsupported %v", x)
			return false
		}
		call.Left = &cc.Expr{Op: cc.Name, Text: "strings.HasPrefix"}
		call.List = call.List[:2]
		call.XType = boolType
		if x.Op == cc.EqEq {
			*x = *call
		} else if x.Op == cc.NotEq {
			x.Op = cc.Not
			x.Right = nil
		}
		x.XType = boolType
		return true

	case "strstr":
		if len(call.List) != 2 {
			fprintf(x.Span, "unsupported %v", x)
			return false
		}
		call.Left = &cc.Expr{Op: cc.Name, Text: "strings.Contains"}
		call.XType = boolType
		if x.Op == cc.NotEq {
			*x = *call
		} else if x.Op == cc.EqEq {
			x.Op = cc.Not
			x.Right = nil
		}
		x.XType = boolType
		return true

	case "utfrune":
		if len(call.List) != 2 {
			fprintf(x.Span, "unsupported %v", x)
			return false
		}
		call.Left = &cc.Expr{Op: cc.Name, Text: "strings.ContainsRune"}
		call.XType = boolType
		if x.Op == cc.NotEq {
			*x = *call
		} else if x.Op == cc.EqEq {
			x.Op = cc.Not
			x.Right = nil
		}
		x.XType = boolType
		return true

	case "ucistrcmp":
		if len(call.List) != 2 {
			fprintf(x.Span, "unsupported %v", x)
			return false
		}
		call.Left = &cc.Expr{Op: cc.Name, Text: "strings.EqualFold"}
		call.XType = boolType
		if x.Op == cc.EqEq {
			*x = *call
		} else if x.Op == cc.NotEq {
			x.Op = cc.Not
			x.Right = nil
		}
		x.XType = boolType
		return true

	case "strcmp":
		if len(call.List) != 2 {
			fprintf(x.Span, "unsupported %v", x)
			return false
		}
		obj1 := call.List[0]
		obj2 := call.List[1]

		x.Left = obj1
		x.Right = obj2
		x.List = nil
		x.XType = boolType
		return true

	case "isspacerune":
		if len(call.List) != 1 {
			fprintf(x.Span, "unsupported %v", x)
			return false
		}
		call.Left.Text = "unicode.IsSpace"
		call.Left.XDecl = nil
		forceConvert(fn, call.List[0], call.List[0].XType, runeType)
		if x.Op == cc.NotEq {
			*x = *call
		} else if x.Op == cc.EqEq {
			x.Op = cc.Not
			x.Right = nil
		}
		x.XType = boolType
		return true
	}

	return false
}
Пример #7
0
// fixArrays rewrites uses of the untyped "Array" container defined in cmd/gc
// to use native Go slices.
// It has nothing to do with standard C arrays.
func fixArray(fn *cc.Decl, x *cc.Expr) {
	// arraynew(n, sizeof(T)) becomes Go make([]T, 0, n).
	if isCall(x, "arraynew") {
		if len(x.List) != 2 {
			fprintf(x.Span, "wrong number of arguments to arraynew")
			return
		}
		if x.List[1].Op != cc.SizeofType {
			fprintf(x.Span, "second argument to arraynew must be sizeof(T)")
			return
		}
		x.Left.Text = "make"
		x.Left.XDecl = nil
		typ := &cc.Type{Kind: Slice, Base: x.List[1].Type}
		x.XType = typ
		x.List = append(x.List, x.List[0])
		x.List[1] = &cc.Expr{Op: cc.Number, Text: "0"}
		x.List[0] = &cc.Expr{Op: ExprType, Type: typ}
		return
	}

	// arraylength(x) becomes len(x)
	if isCall(x, "arraylength") {
		x.Left.Text = "len"
		x.Left.XDecl = nil
		return
	}

	// arrayset is unused in practice!

	// arrayadd(x, &elem) becomes x = append(x, elem).
	// Strictly speaking, this is not a complete translation,
	// because in the C code x might be a pointer taken from
	// another place, and arrayadd changes the len at that
	// other place too. In cmd/gc this does not happen.
	if isCall(x, "arrayadd") {
		if len(x.List) != 2 {
			fprintf(x.Span, "wrong number of arguments to arrayadd")
			return
		}
		if x.List[1].Op != cc.Addr {
			fprintf(x.Span, "second argument to arrayadd must be &x, have %v", x.List[1])
			return
		}
		append := copyExpr(x)
		append.Left.Text = "append"
		append.Left.XDecl = nil
		x.Op = cc.Eq
		x.Left = append.List[0]
		x.Right = append
		append.List[1] = append.List[1].Left
		return
	}

	// *(T**)(arrayget(x, i)) turns into x[i].
	// Record that x should have translated type T*.
	if x.Op == cc.Indir && x.Left.Op == cc.Cast && x.Left.Type.Kind == cc.Ptr && x.Left.Type.Base.Kind == cc.Ptr && isCall(x.Left.Left, "arrayget") {
		call := x.Left.Left
		x.Op = cc.Index
		x.XType = x.Left.Type.Base
		x.Left = call.List[0]
		x.Right = call.List[1]
		saveSliceType(x.Left, x.XType)
		return
	}

	// TODO: arraysort
}