package main import ( _ "fmt" ) func (smith *Smith) initStatements() { smith.statements = []func(){ smith.stmtOas, smith.stmtAs, smith.stmtInc, smith.stmtIf, smith.stmtFor, smith.stmtSend, smith.stmtRecv, smith.stmtSelect, smith.stmtSwitchExpr, smith.stmtSwitchType, smith.stmtTypeDecl, smith.stmtVarDecl, smith.stmtCall, smith.stmtReturn, smith.stmtBreak, smith.stmtContinue, smith.stmtGoto, smith.stmtSink, } } func (smith *Smith) genStatement() { if smith.stmtCount >= NStatements { return } smith.exprCount = 0 smith.stmtCount++ smith.statements[smith.rnd(len(smith.statements))]() } func (smith *Smith) stmtOas() { list := smith.atypeList(TraitAny) str, vars := smith.fmtOasVarList(list) smith.line("%v := %v", str, smith.fmtRvalueList(list)) for _, v := range vars { smith.defineVar(v.id, v.typ) } } func (smith *Smith) stmtReturn() { smith.line("return %v", smith.fmtRvalueList(smith.curFunc.rets)) } func (smith *Smith) stmtAs() { types := smith.atypeList(TraitAny) smith.line("%v = %v", smith.fmtLvalueList(types), smith.fmtRvalueList(types)) } func (smith *Smith) stmtInc() { smith.line("%v %v", smith.lvalueOrMapIndex(smith.atype(ClassNumeric)), smith.choice("--", "++")) } func (smith *Smith) stmtIf() { smith.enterBlock(true) smith.enterBlock(true) if smith.rndBool() { smith.line("if %v {", smith.rvalue(smith.atype(ClassBoolean))) } else { smith.line("if %v; %v {", smith.stmtSimple(true, nil), smith.rvalue(smith.atype(ClassBoolean))) } smith.genBlock() if smith.rndBool() { smith.line("} else {") smith.genBlock() } smith.leaveBlock() smith.line("}") smith.leaveBlock() } func (smith *Smith) stmtFor() { smith.enterBlock(true) smith.enterBlock(true) smith.curBlock.isBreakable = true smith.curBlock.isContinuable = true var vars []*Var switch smith.choice("simple", "complex", "range") { case "simple": smith.line("for %v {", smith.rvalue(smith.atype(ClassBoolean))) case "complex": smith.line("for %v; %v; %v {", smith.stmtSimple(true, nil), smith.rvalue(smith.atype(ClassBoolean)), smith.stmtSimple(false, nil)) case "range": switch smith.choice("slice", "string", "channel", "map") { case "slice": t := smith.atype(TraitAny) s := smith.rvalue(smith.sliceOf(t)) switch smith.choice("one", "two", "oneDecl", "twoDecl") { case "one": smith.line("for %v = range %v {", smith.lvalueOrBlank(smith.intType), s) case "two": smith.line("for %v, %v = range %v {", smith.lvalueOrBlank(smith.intType), smith.lvalueOrBlank(t), s) case "oneDecl": id := smith.newId("Var") smith.line("for %v := range %v {", id, s) vars = append(vars, &Var{id: id, typ: smith.intType}) case "twoDecl": types := []*Type{smith.intType, t} str := "" str, vars = smith.fmtOasVarList(types) smith.line("for %v := range %v {", str, s) default: panic("bad") } case "string": s := smith.rvalue(smith.stringType) switch smith.choice("one", "two", "oneDecl", "twoDecl") { case "one": smith.line("for %v = range %v {", smith.lvalueOrBlank(smith.intType), s) case "two": smith.line("for %v, %v = range %v {", smith.lvalueOrBlank(smith.intType), smith.lvalueOrBlank(smith.runeType), s) case "oneDecl": id := smith.newId("Var") smith.line("for %v := range %v {", id, s) vars = append(vars, &Var{id: id, typ: smith.intType}) case "twoDecl": types := []*Type{smith.intType, smith.runeType} str := "" str, vars = smith.fmtOasVarList(types) smith.line("for %v := range %v {", str, s) default: panic("bad") } case "channel": cht := smith.atype(ClassChan) ch := smith.rvalue(cht) switch smith.choice("one", "oneDecl") { case "one": smith.line("for %v = range %v {", smith.lvalueOrBlank(cht.ktyp), ch) case "oneDecl": id := smith.newId("Var") smith.line("for %v := range %v {", id, ch) vars = append(vars, &Var{id: id, typ: cht.ktyp}) default: panic("bad") } case "map": t := smith.atype(ClassMap) m := smith.rvalue(t) switch smith.choice("one", "two", "oneDecl", "twoDecl") { case "one": smith.line("for %v = range %v {", smith.lvalueOrBlank(t.ktyp), m) case "two": smith.line("for %v, %v = range %v {", smith.lvalueOrBlank(t.ktyp), smith.lvalueOrBlank(t.vtyp), m) case "oneDecl": id := smith.newId("Var") smith.line("for %v := range %v {", id, m) vars = append(vars, &Var{id: id, typ: t.ktyp}) case "twoDecl": types := []*Type{t.ktyp, t.vtyp} str := "" str, vars = smith.fmtOasVarList(types) smith.line("for %v := range %v {", str, m) default: panic("bad") } default: panic("bad") } default: panic("bad") } smith.enterBlock(true) if len(vars) > 0 { smith.line("") for _, v := range vars { smith.defineVar(v.id, v.typ) } } smith.genBlock() smith.leaveBlock() smith.leaveBlock() smith.line("}") smith.leaveBlock() } func (smith *Smith) stmtSimple(oas bool, newVars *[]*Var) string { // We emit a fake statement in "oas", so make sure that nothing can be inserted in between. if smith.curBlock.extendable { panic("bad") } // "send" crashes gccgo with random errors too frequently. // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61273 switch smith.choice("empty", "inc", "assign", "oas", "send", "expr") { case "empty": return "" case "inc": return F("%v %v", smith.lvalueOrMapIndex(smith.atype(ClassNumeric)), smith.choice("--", "++")) case "assign": list := smith.atypeList(TraitAny) return F("%v = %v", smith.fmtLvalueList(list), smith.fmtRvalueList(list)) case "oas": if !oas { return "" } list := smith.atypeList(TraitAny) str, vars := smith.fmtOasVarList(list) if newVars != nil { *newVars = vars } res := F("%v := %v", str, smith.fmtRvalueList(list)) smith.line("") for _, v := range vars { smith.defineVar(v.id, v.typ) } return res case "send": t := smith.atype(TraitSendable) return F("%v <- %v", smith.rvalue(t), smith.rvalue(t.ktyp)) case "expr": return "" default: panic("bad") } } func (smith *Smith) stmtSend() { t := smith.atype(TraitSendable) smith.line("%v <- %v", smith.rvalue(t), smith.rvalue(t.ktyp)) } func (smith *Smith) stmtRecv() { t := smith.atype(TraitReceivable) ch := smith.rvalue(t) switch smith.choice("normal", "decl") { case "normal": smith.line("%v, %v = <-%v", smith.lvalueOrBlank(t.ktyp), smith.lvalueOrBlank(smith.boolType), ch) case "decl": vv := smith.newId("Var") ok := smith.newId("Var") smith.line("%v, %v := <-%v", vv, ok, ch) smith.defineVar(vv, t.ktyp) smith.defineVar(ok, smith.boolType) default: panic("bad") } } func (smith *Smith) stmtTypeDecl() { id := smith.newId("Type") t := smith.atype(TraitAny) smith.line("type %v %v", id, t.id) newTyp := new(Type) *newTyp = *t newTyp.id = id newTyp.namedUserType = true if t.class == ClassStruct { newTyp.literal = func() string { // replace struct name with new type id l := t.literal() l = l[len(t.id)+1:] return "(" + id + l } newTyp.complexLiteral = func() string { // replace struct name with new type id l := t.complexLiteral() l = l[len(t.id)+1:] return "(" + id + l } } else { newTyp.literal = func() string { return F("%v(%v)", id, t.literal()) } if t.complexLiteral != nil { newTyp.complexLiteral = func() string { return F("%v(%v)", id, t.complexLiteral()) } } } smith.defineType(newTyp) } func (smith *Smith) stmtVarDecl() { id := smith.newId("Var") t := smith.atype(TraitAny) smith.line("var %v %v = %v", id, t.id, smith.rvalue(t)) smith.defineVar(id, t) } func (smith *Smith) stmtSelect() { smith.enterBlock(true) smith.line("select {") for smith.rnd(5) != 0 { smith.enterBlock(true) elem := smith.atype(TraitAny) cht := smith.chanOf(elem) ch := smith.rvalue(cht) if smith.rndBool() { smith.line("case %v <- %v:", ch, smith.rvalue(elem)) } else { switch smith.choice("one", "two", "oneDecl", "twoDecl") { case "one": smith.line("case %v = <-%v:", smith.lvalueOrBlank(elem), ch) case "two": smith.line("case %v, %v = <-%v:", smith.lvalueOrBlank(elem), smith.lvalueOrBlank(smith.boolType), ch) case "oneDecl": vv := smith.newId("Var") smith.line("case %v := <-%v:", vv, ch) smith.defineVar(vv, elem) case "twoDecl": vv := smith.newId("Var") ok := smith.newId("Var") smith.line("case %v, %v := <-%v:", vv, ok, ch) smith.defineVar(vv, elem) smith.defineVar(ok, smith.boolType) default: panic("bad") } } smith.genBlock() smith.leaveBlock() } if smith.rndBool() { smith.enterBlock(true) smith.line("default:") smith.genBlock() smith.leaveBlock() } smith.line("}") smith.leaveBlock() } func (smith *Smith) stmtSwitchExpr() { var t *Type cond := "" if smith.rndBool() { t = smith.atype(TraitComparable) cond = smith.rvalue(t) } else { t = smith.boolType } smith.enterBlock(true) smith.enterBlock(true) smith.curBlock.isBreakable = true var vars []*Var if smith.rndBool() { smith.line("switch %v {", cond) } else { smith.line("switch %v; %v {", smith.stmtSimple(true, &vars), cond) } // TODO: we generate at most one case, because if we generate more, // we can generate two cases with equal constants. fallth := false if smith.rndBool() { smith.enterBlock(true) smith.line("case %v:", smith.rvalue(t)) smith.genBlock() smith.leaveBlock() if smith.rndBool() { fallth = true smith.line("fallthrough") } } if fallth || len(vars) > 0 || smith.rndBool() { smith.enterBlock(true) smith.line("default:") smith.genBlock() for _, v := range vars { smith.line("_ = %v", v.id) v.used = true } smith.leaveBlock() } smith.leaveBlock() smith.line("}") smith.leaveBlock() } func (smith *Smith) stmtSwitchType() { cond := smith.lvalue(smith.atype(TraitAny)) smith.enterBlock(true) smith.curBlock.isBreakable = true smith.line("switch COND := (interface{})(%v); COND.(type) {", cond) if smith.rndBool() { smith.enterBlock(true) smith.line("case %v:", smith.atype(TraitAny).id) smith.genBlock() smith.leaveBlock() } if smith.rndBool() { smith.enterBlock(true) smith.line("default:") smith.genBlock() smith.leaveBlock() } smith.line("}") smith.leaveBlock() } func (smith *Smith) stmtCall() { if smith.rndBool() { smith.stmtCallBuiltin() } t := smith.atype(ClassFunction) prefix := smith.choice("", "go", "defer") smith.line("%v %v(%v)", prefix, smith.rvalue(t), smith.fmtRvalueList(t.styp)) } func (smith *Smith) stmtCallBuiltin() { prefix := smith.choice("", "go", "defer") switch fn := smith.choice("close", "copy", "delete", "panic", "print", "println", "recover"); fn { case "close": smith.line("%v %v(%v)", prefix, fn, smith.rvalue(smith.atype(ClassChan))) case "copy": smith.line("%v %v", prefix, smith.exprCopySlice()) case "delete": t := smith.atype(ClassMap) smith.line("%v %v(%v, %v)", prefix, fn, smith.rvalue(t), smith.rvalue(t.ktyp)) case "panic": smith.line("%v %v(%v)", prefix, fn, smith.rvalue(smith.atype(TraitAny))) case "print": fallthrough case "println": list := smith.atypeList(TraitPrintable) smith.line("%v %v(%v)", prefix, fn, smith.fmtRvalueList(list)) case "recover": smith.line("%v %v()", prefix, fn) default: panic("bad") } } func (smith *Smith) stmtBreak() { if !smith.curBlock.isBreakable { return } smith.line("break") } func (smith *Smith) stmtContinue() { if !smith.curBlock.isContinuable { return } smith.line("continue") } func (smith *Smith) stmtGoto() { // TODO: suppport goto down id := smith.materializeGotoLabel() smith.line("goto %v", id) } func (smith *Smith) stmtSink() { // Makes var escape. smith.line("SINK = %v", smith.exprVar(smith.atype(TraitAny))) }