package main import ( "bytes" "fmt" ) func (smith *Smith) initExpressions() { smith.expressions = []func(res *Type) string{ smith.exprLiteral, smith.exprVar, smith.exprFunc, smith.exprSelectorField, smith.exprRecv, smith.exprArith, smith.exprEqual, smith.exprOrder, smith.exprCall, smith.exprCallBuiltin, smith.exprAddress, smith.exprDeref, smith.exprSlice, smith.exprIndexSlice, smith.exprIndexArray, smith.exprIndexString, smith.exprIndexMap, smith.exprConversion, } } func (smith *Smith) expression(res *Type) string { smith.exprCount++ smith.totalExprCount++ if smith.exprDepth >= NExprDepth || smith.exprCount >= NExprCount || smith.totalExprCount >= NTotalExprCount { return res.literal() } for { smith.exprDepth++ s := smith.expressions[smith.rnd(len(smith.expressions))](res) smith.exprDepth-- if s != "" { return s } } } func (smith *Smith) rvalue(t *Type) string { return smith.expression(t) } // rvalue, but not a const // used to index arrays and strings func (smith *Smith) nonconstRvalue(t *Type) string { if t.class != ClassNumeric { panic("bad") } trying: for { res := "" switch smith.choice("lvalue", "call", "len", "selector", "recv", "arith", "indexMap", "conv") { case "lvalue": res = smith.lvalue(t) case "call": res = smith.exprCall(t) case "len": tt := smith.atype(TraitLenCapable) fn := smith.choice("len", "cap") if (tt.class == ClassString || tt.class == ClassMap) && fn == "cap" { break } if tt.class == ClassArray { // len/cap are const break } res = F("(%v)((%v)(%v))", t.id, fn, smith.lvalue(tt)) case "selector": res = smith.exprSelectorField(t) case "recv": res = smith.exprRecv(t) case "arith": res = F("(%v) %v (%v)", smith.lvalue(t), smith.choice("+", "-"), smith.rvalue(t)) case "indexMap": res = smith.exprIndexMap(t) case "conv": res = F("(%v)(%v %v)", t.id, smith.lvalue(smith.atype(ClassNumeric)), smith.choice("", ",")) default: panic("bad") } if res == "" { continue trying } return res } } func (smith *Smith) lvalue(t *Type) string { for { switch smith.choice("var", "indexSlice", "indexArray", "selector", "deref") { case "var": return smith.exprVar(t) case "indexSlice": return smith.exprIndexSlice(t) case "indexArray": return F("(%v)[%v]", smith.lvalue(smith.arrayOf(t)), smith.nonconstRvalue(smith.intType)) case "selector": for i := 0; i < 10; i++ { st := smith.atype(ClassStruct) for _, e := range st.elems { if e.typ == t { return F("(%v).%v", smith.lvalue(st), e.id) } } } continue case "deref": return smith.exprDeref(t) default: panic("bad") } } } func (smith *Smith) lvalueOrBlank(t *Type) string { for { switch smith.choice("lvalue", "map", "blank") { case "lvalue": return smith.lvalue(t) case "map": if e := smith.exprIndexMap(t); e != "" { return e } case "blank": return "_" default: panic("bad") } } } func (smith *Smith) lvalueOrMapIndex(t *Type) string { for { switch smith.choice("lvalue", "map") { case "lvalue": return smith.lvalue(t) case "map": if e := smith.exprIndexMap(t); e != "" { return e } default: panic("bad") } } } func (smith *Smith) fmtRvalueList(list []*Type) string { var buf bytes.Buffer for i, t := range list { if i != 0 { buf.Write([]byte{','}) } fmt.Fprintf(&buf, "%v", smith.rvalue(t)) } return buf.String() } func (smith *Smith) fmtLvalueList(list []*Type) string { var buf bytes.Buffer for i, t := range list { if i != 0 { buf.Write([]byte{','}) } buf.WriteString(smith.lvalueOrBlank(t)) } return buf.String() } func (smith *Smith) fmtOasVarList(list []*Type) (str string, newVars []*Var) { allVars := smith.vars() var buf bytes.Buffer for i, t := range list { expr := "_" // First, try to find an existing var in the same scope. if smith.rndBool() { for i, v := range allVars { if v.typ == t && v.block == smith.curBlock { allVars[i] = allVars[len(allVars)-1] allVars = allVars[:len(allVars)-1] expr = v.id break } } } if smith.rndBool() || (i == len(list)-1 && len(newVars) == 0) { expr = smith.newId("Var") newVars = append(newVars, &Var{id: expr, typ: t}) } if i != 0 { buf.WriteString(", ") } buf.WriteString(expr) } return buf.String(), newVars } func (smith *Smith) exprLiteral(res *Type) string { if res.complexLiteral != nil { return res.complexLiteral() } return res.literal() } func (smith *Smith) exprVar(res *Type) string { for _, v := range smith.vars() { if v.typ == res { return v.id } } return smith.materializeVar(res) } func (smith *Smith) exprSelectorField(res *Type) string { for i := 0; i < 10; i++ { st := smith.atype(ClassStruct) for _, e := range st.elems { if e.typ == res { return F("(%v).%v", smith.rvalue(st), e.id) } } } return "" } func (smith *Smith) exprFunc(res *Type) string { if !smith.satisfiesTrait(res, TraitGlobal) { return "" } var f *Func for _, f1 := range smith.packages[smith.curPackage].toplevFuncs { if len(f1.rets) == 1 && f1.rets[0] == res { f = f1 break } } if f == nil { f = smith.materializeFunc([]*Type{res}) } if smith.rndBool() { return F("%v(%v)", f.name, smith.fmtRvalueList(f.args)) } else { var f0 *Func loop: for _, f1 := range smith.packages[smith.curPackage].toplevFuncs { if len(f1.rets) == len(f.args) { for i := range f.args { // TODO: check assignability if f1.rets[i] != f.args[i] { continue loop } } f0 = f1 break } } if f0 == nil { f0 = smith.materializeFunc(f.args) } return F("%v(%v(%v))", f.name, f0.name, smith.fmtRvalueList(f0.args)) } } func (smith *Smith) exprAddress(res *Type) string { if res.class != ClassPointer { return "" } if res.ktyp.class == ClassStruct && smith.rndBool() { return F("&%v", res.ktyp.complexLiteral()) } return F("(%v)(&(%v))", res.id, smith.lvalue(res.ktyp)) } func (smith *Smith) exprDeref(res *Type) string { return F("(*(%v))", smith.lvalue(pointerTo(res))) } func (smith *Smith) exprRecv(res *Type) string { t := smith.chanOf(res) return F("(<- %v)", smith.rvalue(t)) } func (smith *Smith) exprArith(res *Type) string { if res.class != ClassNumeric && res.class != ClassComplex { return "" } // "/" causes division by zero // "*" causes generation of -1 index in int(real(1i * 1i)) return F("(%v) + (%v)", smith.rvalue(res), smith.rvalue(res)) } func (smith *Smith) exprEqual(res *Type) string { if res != smith.boolType { return "" } t := smith.atype(TraitComparable) return F("(%v) %v (%v)", smith.rvalue(t), smith.choice("==", "!="), smith.rvalue(t)) } func (smith *Smith) exprOrder(res *Type) string { if res != smith.boolType { return "" } t := smith.atype(TraitOrdered) return F("(%v) %v (%v)", smith.rvalue(t), smith.choice("<", "<=", ">", ">="), smith.rvalue(t)) } func (smith *Smith) exprCall(ret *Type) string { t := smith.funcOf(smith.atypeList(TraitAny), []*Type{ret}) return F("%v(%v)", smith.rvalue(t), smith.fmtRvalueList(t.styp)) } func (smith *Smith) exprCallBuiltin(ret *Type) string { switch fn := smith.choice("append", "cap", "complex", "copy", "imag", "len", "make", "new", "real", "recover"); fn { case "append": if ret.class != ClassSlice { return "" } switch smith.choice("one", "two", "slice") { case "one": return F("%v(%v, %v)", fn, smith.rvalue(ret), smith.rvalue(ret.ktyp)) case "two": return F("%v(%v, %v, %v)", fn, smith.rvalue(ret), smith.rvalue(ret.ktyp), smith.rvalue(ret.ktyp)) case "slice": return F("%v(%v, %v...)", fn, smith.rvalue(ret), smith.rvalue(ret)) default: panic("bad") } case "len", "cap": if ret != smith.intType { // TODO: must be convertable return "" } t := smith.atype(TraitLenCapable) if (t.class == ClassString || t.class == ClassMap) && fn == "cap" { return "" } return F("%v(%v)", fn, smith.rvalue(t)) case "copy": if ret != smith.intType { return "" } return F("%v", smith.exprCopySlice()) case "make": if ret.class != ClassSlice && ret.class != ClassMap && ret.class != ClassChan { return "" } cap := "" if ret.class == ClassSlice { if smith.rndBool() { cap = F(", %v", smith.rvalue(smith.intType)) } else { // Careful to not generate "len larger than cap". cap = F(", 0, %v", smith.rvalue(smith.intType)) } } else if smith.rndBool() { cap = F(", %v", smith.rvalue(smith.intType)) } return F("make(%v %v)", ret.id, cap) case "new": if ret.class != ClassPointer { return "" } return F("new(%v)", ret.ktyp.id) case "recover": if ret != smith.efaceType { return "" } return "recover()" case "real", "imag": if ret == smith.float32Type { return F("real(%v)", smith.rvalue(smith.complex64Type)) } if ret == smith.float64Type { return F("real(%v)", smith.rvalue(smith.complex128Type)) } return "" case "complex": if ret == smith.complex64Type { return F("complex(%v, %v)", smith.rvalue(smith.float32Type), smith.rvalue(smith.float32Type)) } if ret == smith.complex128Type { return F("complex(%v, %v)", smith.rvalue(smith.float64Type), smith.rvalue(smith.float64Type)) } return "" default: panic("bad") } } func (smith *Smith) exprCopySlice() string { if smith.rndBool() { t := smith.atype(ClassSlice) return F("copy(%v, %v)", smith.rvalue(t), smith.rvalue(t)) } else { return F("copy(%v, %v)", smith.rvalue(smith.sliceOf(smith.byteType)), smith.rvalue(smith.stringType)) } } func (smith *Smith) exprSlice(ret *Type) string { if ret.class != ClassSlice { return "" } i0 := "" if smith.rndBool() { i0 = smith.nonconstRvalue(smith.intType) } i2 := "" if smith.rndBool() { i2 = ":" + smith.nonconstRvalue(smith.intType) } i1 := ":" if smith.rndBool() || i2 != "" { i1 = ":" + smith.nonconstRvalue(smith.intType) } return F("(%v)[%v%v%v]", smith.rvalue(ret), i0, i1, i2) } func (smith *Smith) exprIndexSlice(ret *Type) string { return F("(%v)[%v]", smith.rvalue(smith.sliceOf(ret)), smith.nonconstRvalue(smith.intType)) } func (smith *Smith) exprIndexString(ret *Type) string { if ret != smith.byteType { return "" } return F("(%v)[%v]", smith.rvalue(smith.stringType), smith.nonconstRvalue(smith.intType)) } func (smith *Smith) exprIndexArray(ret *Type) string { // TODO: also handle indexing of pointers to arrays return F("(%v)[%v]", smith.rvalue(smith.arrayOf(ret)), smith.nonconstRvalue(smith.intType)) } func (smith *Smith) exprIndexMap(ret *Type) string { // TODO: figure out something better for i := 0; i < 10; i++ { t := smith.atype(ClassMap) if t.vtyp == ret { return F("(%v)[%v]", smith.rvalue(t), smith.rvalue(t.ktyp)) } } return "" } func (smith *Smith) exprConversion(ret *Type) string { if ret.class == ClassNumeric { return F("(%v)(%v %v)", ret.id, smith.rvalue(smith.atype(ClassNumeric)), smith.choice("", ",")) } if ret.class == ClassComplex { return F("(%v)(%v %v)", ret.id, smith.rvalue(smith.atype(ClassComplex)), smith.choice("", ",")) } if ret == smith.stringType { switch smith.choice("int", "byteSlice", "runeSlice") { case "int": // We produce a string of length at least 3, to not produce // "invalid string index 1 (out of bounds for 1-byte string)" return F("(%v)((%v) + (1<<24) %v)", ret.id, smith.rvalue(smith.intType), smith.choice("", ",")) case "byteSlice": return F("(%v)(%v %v)", ret.id, smith.rvalue(smith.sliceOf(smith.byteType)), smith.choice("", ",")) case "runeSlice": return F("(%v)(%v %v)", ret.id, smith.rvalue(smith.sliceOf(smith.runeType)), smith.choice("", ",")) default: panic("bad") } } if ret.class == ClassSlice && (ret.ktyp == smith.byteType || ret.ktyp == smith.runeType) { return F("(%v)(%v %v)", ret.id, smith.rvalue(smith.stringType), smith.choice("", ",")) } // TODO: handle "x is assignable to T" // TODO: handle "x's type and T have identical underlying types" // TODO: handle "x's type and T are unnamed pointer types and their pointer base types have identical underlying types" return "" }