1package main
2
3import (
4	_ "fmt"
5)
6
7func (smith *Smith) initStatements() {
8	smith.statements = []func(){
9		smith.stmtOas,
10		smith.stmtAs,
11		smith.stmtInc,
12		smith.stmtIf,
13		smith.stmtFor,
14		smith.stmtSend,
15		smith.stmtRecv,
16		smith.stmtSelect,
17		smith.stmtSwitchExpr,
18		smith.stmtSwitchType,
19		smith.stmtTypeDecl,
20		smith.stmtVarDecl,
21		smith.stmtCall,
22		smith.stmtReturn,
23		smith.stmtBreak,
24		smith.stmtContinue,
25		smith.stmtGoto,
26		smith.stmtSink,
27	}
28}
29
30func (smith *Smith) genStatement() {
31	if smith.stmtCount >= NStatements {
32		return
33	}
34	smith.exprCount = 0
35	smith.stmtCount++
36	smith.statements[smith.rnd(len(smith.statements))]()
37}
38
39func (smith *Smith) stmtOas() {
40	list := smith.atypeList(TraitAny)
41	str, vars := smith.fmtOasVarList(list)
42	smith.line("%v := %v", str, smith.fmtRvalueList(list))
43	for _, v := range vars {
44		smith.defineVar(v.id, v.typ)
45	}
46}
47
48func (smith *Smith) stmtReturn() {
49	smith.line("return %v", smith.fmtRvalueList(smith.curFunc.rets))
50}
51
52func (smith *Smith) stmtAs() {
53	types := smith.atypeList(TraitAny)
54	smith.line("%v = %v", smith.fmtLvalueList(types), smith.fmtRvalueList(types))
55}
56
57func (smith *Smith) stmtInc() {
58	smith.line("%v %v", smith.lvalueOrMapIndex(smith.atype(ClassNumeric)), smith.choice("--", "++"))
59}
60
61func (smith *Smith) stmtIf() {
62	smith.enterBlock(true)
63	smith.enterBlock(true)
64	if smith.rndBool() {
65		smith.line("if %v {", smith.rvalue(smith.atype(ClassBoolean)))
66	} else {
67		smith.line("if %v; %v {", smith.stmtSimple(true, nil), smith.rvalue(smith.atype(ClassBoolean)))
68	}
69	smith.genBlock()
70	if smith.rndBool() {
71		smith.line("} else {")
72		smith.genBlock()
73	}
74	smith.leaveBlock()
75	smith.line("}")
76	smith.leaveBlock()
77}
78
79func (smith *Smith) stmtFor() {
80	smith.enterBlock(true)
81	smith.enterBlock(true)
82	smith.curBlock.isBreakable = true
83	smith.curBlock.isContinuable = true
84	var vars []*Var
85	switch smith.choice("simple", "complex", "range") {
86	case "simple":
87		smith.line("for %v {", smith.rvalue(smith.atype(ClassBoolean)))
88	case "complex":
89		smith.line("for %v; %v; %v {", smith.stmtSimple(true, nil), smith.rvalue(smith.atype(ClassBoolean)), smith.stmtSimple(false, nil))
90	case "range":
91		switch smith.choice("slice", "string", "channel", "map") {
92		case "slice":
93			t := smith.atype(TraitAny)
94			s := smith.rvalue(smith.sliceOf(t))
95			switch smith.choice("one", "two", "oneDecl", "twoDecl") {
96			case "one":
97				smith.line("for %v = range %v {", smith.lvalueOrBlank(smith.intType), s)
98			case "two":
99				smith.line("for %v, %v = range %v {", smith.lvalueOrBlank(smith.intType), smith.lvalueOrBlank(t), s)
100			case "oneDecl":
101				id := smith.newId("Var")
102				smith.line("for %v := range %v {", id, s)
103				vars = append(vars, &Var{id: id, typ: smith.intType})
104			case "twoDecl":
105				types := []*Type{smith.intType, t}
106				str := ""
107				str, vars = smith.fmtOasVarList(types)
108				smith.line("for %v := range %v {", str, s)
109			default:
110				panic("bad")
111			}
112		case "string":
113			s := smith.rvalue(smith.stringType)
114			switch smith.choice("one", "two", "oneDecl", "twoDecl") {
115			case "one":
116				smith.line("for %v = range %v {", smith.lvalueOrBlank(smith.intType), s)
117			case "two":
118				smith.line("for %v, %v = range %v {", smith.lvalueOrBlank(smith.intType), smith.lvalueOrBlank(smith.runeType), s)
119			case "oneDecl":
120				id := smith.newId("Var")
121				smith.line("for %v := range %v {", id, s)
122				vars = append(vars, &Var{id: id, typ: smith.intType})
123			case "twoDecl":
124				types := []*Type{smith.intType, smith.runeType}
125				str := ""
126				str, vars = smith.fmtOasVarList(types)
127				smith.line("for %v := range %v {", str, s)
128			default:
129				panic("bad")
130			}
131		case "channel":
132			cht := smith.atype(ClassChan)
133			ch := smith.rvalue(cht)
134			switch smith.choice("one", "oneDecl") {
135			case "one":
136				smith.line("for %v = range %v {", smith.lvalueOrBlank(cht.ktyp), ch)
137			case "oneDecl":
138				id := smith.newId("Var")
139				smith.line("for %v := range %v {", id, ch)
140				vars = append(vars, &Var{id: id, typ: cht.ktyp})
141			default:
142				panic("bad")
143			}
144		case "map":
145			t := smith.atype(ClassMap)
146			m := smith.rvalue(t)
147			switch smith.choice("one", "two", "oneDecl", "twoDecl") {
148			case "one":
149				smith.line("for %v = range %v {", smith.lvalueOrBlank(t.ktyp), m)
150			case "two":
151				smith.line("for %v, %v = range %v {", smith.lvalueOrBlank(t.ktyp), smith.lvalueOrBlank(t.vtyp), m)
152			case "oneDecl":
153				id := smith.newId("Var")
154				smith.line("for %v := range %v {", id, m)
155				vars = append(vars, &Var{id: id, typ: t.ktyp})
156			case "twoDecl":
157				types := []*Type{t.ktyp, t.vtyp}
158				str := ""
159				str, vars = smith.fmtOasVarList(types)
160				smith.line("for %v := range %v {", str, m)
161			default:
162				panic("bad")
163			}
164		default:
165			panic("bad")
166		}
167	default:
168		panic("bad")
169	}
170	smith.enterBlock(true)
171	if len(vars) > 0 {
172		smith.line("")
173		for _, v := range vars {
174			smith.defineVar(v.id, v.typ)
175		}
176	}
177	smith.genBlock()
178	smith.leaveBlock()
179	smith.leaveBlock()
180	smith.line("}")
181	smith.leaveBlock()
182}
183
184func (smith *Smith) stmtSimple(oas bool, newVars *[]*Var) string {
185	// We emit a fake statement in "oas", so make sure that nothing can be inserted in between.
186	if smith.curBlock.extendable {
187		panic("bad")
188	}
189	// "send" crashes gccgo with random errors too frequently.
190	// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61273
191	switch smith.choice("empty", "inc", "assign", "oas", "send", "expr") {
192	case "empty":
193		return ""
194	case "inc":
195		return F("%v %v", smith.lvalueOrMapIndex(smith.atype(ClassNumeric)), smith.choice("--", "++"))
196	case "assign":
197		list := smith.atypeList(TraitAny)
198		return F("%v = %v", smith.fmtLvalueList(list), smith.fmtRvalueList(list))
199	case "oas":
200		if !oas {
201			return ""
202		}
203		list := smith.atypeList(TraitAny)
204		str, vars := smith.fmtOasVarList(list)
205		if newVars != nil {
206			*newVars = vars
207		}
208		res := F("%v := %v", str, smith.fmtRvalueList(list))
209		smith.line("")
210		for _, v := range vars {
211			smith.defineVar(v.id, v.typ)
212		}
213		return res
214	case "send":
215		t := smith.atype(TraitSendable)
216		return F("%v <- %v", smith.rvalue(t), smith.rvalue(t.ktyp))
217	case "expr":
218		return ""
219	default:
220		panic("bad")
221	}
222}
223
224func (smith *Smith) stmtSend() {
225	t := smith.atype(TraitSendable)
226	smith.line("%v <- %v", smith.rvalue(t), smith.rvalue(t.ktyp))
227}
228
229func (smith *Smith) stmtRecv() {
230	t := smith.atype(TraitReceivable)
231	ch := smith.rvalue(t)
232	switch smith.choice("normal", "decl") {
233	case "normal":
234		smith.line("%v, %v = <-%v", smith.lvalueOrBlank(t.ktyp), smith.lvalueOrBlank(smith.boolType), ch)
235	case "decl":
236		vv := smith.newId("Var")
237		ok := smith.newId("Var")
238		smith.line("%v, %v := <-%v", vv, ok, ch)
239		smith.defineVar(vv, t.ktyp)
240		smith.defineVar(ok, smith.boolType)
241	default:
242		panic("bad")
243	}
244}
245
246func (smith *Smith) stmtTypeDecl() {
247	id := smith.newId("Type")
248	t := smith.atype(TraitAny)
249	smith.line("type %v %v", id, t.id)
250
251	newTyp := new(Type)
252	*newTyp = *t
253	newTyp.id = id
254	newTyp.namedUserType = true
255	if t.class == ClassStruct {
256		newTyp.literal = func() string {
257			// replace struct name with new type id
258			l := t.literal()
259			l = l[len(t.id)+1:]
260			return "(" + id + l
261		}
262		newTyp.complexLiteral = func() string {
263			// replace struct name with new type id
264			l := t.complexLiteral()
265			l = l[len(t.id)+1:]
266			return "(" + id + l
267		}
268	} else {
269		newTyp.literal = func() string {
270			return F("%v(%v)", id, t.literal())
271		}
272		if t.complexLiteral != nil {
273			newTyp.complexLiteral = func() string {
274				return F("%v(%v)", id, t.complexLiteral())
275			}
276		}
277	}
278	smith.defineType(newTyp)
279}
280
281func (smith *Smith) stmtVarDecl() {
282	id := smith.newId("Var")
283	t := smith.atype(TraitAny)
284	smith.line("var %v %v = %v", id, t.id, smith.rvalue(t))
285	smith.defineVar(id, t)
286}
287
288func (smith *Smith) stmtSelect() {
289	smith.enterBlock(true)
290	smith.line("select {")
291	for smith.rnd(5) != 0 {
292		smith.enterBlock(true)
293		elem := smith.atype(TraitAny)
294		cht := smith.chanOf(elem)
295		ch := smith.rvalue(cht)
296		if smith.rndBool() {
297			smith.line("case %v <- %v:", ch, smith.rvalue(elem))
298		} else {
299			switch smith.choice("one", "two", "oneDecl", "twoDecl") {
300			case "one":
301				smith.line("case %v = <-%v:", smith.lvalueOrBlank(elem), ch)
302			case "two":
303				smith.line("case %v, %v = <-%v:", smith.lvalueOrBlank(elem), smith.lvalueOrBlank(smith.boolType), ch)
304			case "oneDecl":
305				vv := smith.newId("Var")
306				smith.line("case %v := <-%v:", vv, ch)
307				smith.defineVar(vv, elem)
308			case "twoDecl":
309				vv := smith.newId("Var")
310				ok := smith.newId("Var")
311				smith.line("case %v, %v := <-%v:", vv, ok, ch)
312				smith.defineVar(vv, elem)
313				smith.defineVar(ok, smith.boolType)
314			default:
315				panic("bad")
316			}
317		}
318		smith.genBlock()
319		smith.leaveBlock()
320	}
321	if smith.rndBool() {
322		smith.enterBlock(true)
323		smith.line("default:")
324		smith.genBlock()
325		smith.leaveBlock()
326	}
327	smith.line("}")
328	smith.leaveBlock()
329}
330
331func (smith *Smith) stmtSwitchExpr() {
332	var t *Type
333	cond := ""
334	if smith.rndBool() {
335		t = smith.atype(TraitComparable)
336		cond = smith.rvalue(t)
337	} else {
338		t = smith.boolType
339	}
340	smith.enterBlock(true)
341	smith.enterBlock(true)
342	smith.curBlock.isBreakable = true
343	var vars []*Var
344	if smith.rndBool() {
345		smith.line("switch %v {", cond)
346	} else {
347		smith.line("switch %v; %v {", smith.stmtSimple(true, &vars), cond)
348	}
349	// TODO: we generate at most one case, because if we generate more,
350	// we can generate two cases with equal constants.
351	fallth := false
352	if smith.rndBool() {
353		smith.enterBlock(true)
354		smith.line("case %v:", smith.rvalue(t))
355		smith.genBlock()
356		smith.leaveBlock()
357		if smith.rndBool() {
358			fallth = true
359			smith.line("fallthrough")
360		}
361	}
362	if fallth || len(vars) > 0 || smith.rndBool() {
363		smith.enterBlock(true)
364		smith.line("default:")
365		smith.genBlock()
366		for _, v := range vars {
367			smith.line("_ = %v", v.id)
368			v.used = true
369		}
370		smith.leaveBlock()
371	}
372	smith.leaveBlock()
373	smith.line("}")
374	smith.leaveBlock()
375}
376
377func (smith *Smith) stmtSwitchType() {
378	cond := smith.lvalue(smith.atype(TraitAny))
379	smith.enterBlock(true)
380	smith.curBlock.isBreakable = true
381	smith.line("switch COND := (interface{})(%v); COND.(type) {", cond)
382	if smith.rndBool() {
383		smith.enterBlock(true)
384		smith.line("case %v:", smith.atype(TraitAny).id)
385		smith.genBlock()
386		smith.leaveBlock()
387	}
388	if smith.rndBool() {
389		smith.enterBlock(true)
390		smith.line("default:")
391		smith.genBlock()
392		smith.leaveBlock()
393	}
394	smith.line("}")
395	smith.leaveBlock()
396}
397
398func (smith *Smith) stmtCall() {
399	if smith.rndBool() {
400		smith.stmtCallBuiltin()
401	}
402	t := smith.atype(ClassFunction)
403	prefix := smith.choice("", "go", "defer")
404	smith.line("%v %v(%v)", prefix, smith.rvalue(t), smith.fmtRvalueList(t.styp))
405}
406
407func (smith *Smith) stmtCallBuiltin() {
408	prefix := smith.choice("", "go", "defer")
409	switch fn := smith.choice("close", "copy", "delete", "panic", "print", "println", "recover"); fn {
410	case "close":
411		smith.line("%v %v(%v)", prefix, fn, smith.rvalue(smith.atype(ClassChan)))
412	case "copy":
413		smith.line("%v %v", prefix, smith.exprCopySlice())
414	case "delete":
415		t := smith.atype(ClassMap)
416		smith.line("%v %v(%v, %v)", prefix, fn, smith.rvalue(t), smith.rvalue(t.ktyp))
417	case "panic":
418		smith.line("%v %v(%v)", prefix, fn, smith.rvalue(smith.atype(TraitAny)))
419	case "print":
420		fallthrough
421	case "println":
422		list := smith.atypeList(TraitPrintable)
423		smith.line("%v %v(%v)", prefix, fn, smith.fmtRvalueList(list))
424	case "recover":
425		smith.line("%v %v()", prefix, fn)
426	default:
427		panic("bad")
428	}
429}
430
431func (smith *Smith) stmtBreak() {
432	if !smith.curBlock.isBreakable {
433		return
434	}
435	smith.line("break")
436}
437
438func (smith *Smith) stmtContinue() {
439	if !smith.curBlock.isContinuable {
440		return
441	}
442	smith.line("continue")
443}
444
445func (smith *Smith) stmtGoto() {
446	// TODO: suppport goto down
447	id := smith.materializeGotoLabel()
448	smith.line("goto %v", id)
449}
450
451func (smith *Smith) stmtSink() {
452	// Makes var escape.
453	smith.line("SINK = %v", smith.exprVar(smith.atype(TraitAny)))
454}
455