Skip to content

Commit b53e786

Browse files
committed
all: reduce usage of global randomness source
Also introduce a generic RandItem function that returns a random item from any slice. Bump go version to 1.18.
1 parent 66bfc6f commit b53e786

File tree

7 files changed

+231
-229
lines changed

7 files changed

+231
-229
lines changed

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module github.com/ALTree/microsmith
22

3-
go 1.14
3+
go 1.18

microsmith/context.go

+160-149
Original file line numberDiff line numberDiff line change
@@ -1,149 +1,160 @@
1-
package microsmith
2-
3-
import (
4-
"math/rand"
5-
"strconv"
6-
)
7-
8-
// Context holds all the contextual information needed while
9-
// generating a random package.
10-
type Context struct { // TODO(alb): rename to PackageContext
11-
12-
// program-wide settings for the fuzzer
13-
programConf ProgramConf
14-
15-
// all the Costraints available declared in the package
16-
constraints []Constraint
17-
18-
// package-wide scope of vars and func available to the code in a
19-
// given moment
20-
scope *Scope
21-
22-
// Function-level scope of the type parameters available to the
23-
// code in the body. Reset and re-filled in when declaring a new
24-
// function.
25-
typeparams *Scope
26-
}
27-
28-
func NewContext(pc ProgramConf) *Context {
29-
return &Context{
30-
programConf: pc,
31-
}
32-
}
33-
34-
// ProgramConf holds program-wide configuration settings that change
35-
// the kind of programs that are generated.
36-
type ProgramConf struct {
37-
MultiPkg bool // for -multipkg
38-
TypeParams bool // for -tp
39-
}
40-
41-
// --------------------------------
42-
// Types Randomizers
43-
// --------------------------------
44-
45-
// Returns a slice of n random types, including composite types
46-
// (structs, array, maps, chans).
47-
func (pb PackageBuilder) RandTypes(n int) []Type {
48-
types := make([]Type, n)
49-
for i := 0; i < n; i++ {
50-
types[i] = pb.RandType()
51-
}
52-
return types
53-
}
54-
55-
// Returns a single random type (including structs, array, maps,
56-
// chans).
57-
func (pb PackageBuilder) RandType() Type {
58-
pb.typedepth++
59-
defer func() { pb.typedepth-- }()
60-
61-
if pb.typedepth >= 5 {
62-
return pb.RandBaseType()
63-
}
64-
65-
switch pb.rs.Intn(15) {
66-
case 0, 1:
67-
return ArrayOf(pb.RandType())
68-
case 2:
69-
return ChanOf(pb.RandType())
70-
case 3, 4:
71-
return MapOf(
72-
pb.RandAddressableType(),
73-
pb.RandType(),
74-
)
75-
case 5, 6:
76-
return PointerOf(pb.RandType())
77-
case 7, 8:
78-
return pb.RandStructType()
79-
case 9:
80-
return pb.RandFuncType()
81-
default:
82-
return pb.RandBaseType()
83-
}
84-
}
85-
86-
func (pb PackageBuilder) RandAddressableType() Type {
87-
types := make([]Type, 0, 32)
88-
89-
// collect addressable Base Types
90-
for _, t := range pb.baseTypes {
91-
if t.Addressable() {
92-
types = append(types, t)
93-
}
94-
}
95-
96-
// look for addressable type parameters
97-
if tp := pb.ctx.typeparams; tp != nil {
98-
for _, v := range *tp {
99-
if v.Type.Addressable() {
100-
types = append(types, MakeTypeParam(v))
101-
}
102-
}
103-
}
104-
105-
return types[pb.rs.Intn(len(types))]
106-
}
107-
108-
// Returns a single BaseType (primitives, or a type parameter).
109-
func (pb PackageBuilder) RandBaseType() Type {
110-
if tp := pb.ctx.typeparams; tp != nil {
111-
i := pb.rs.Intn(len(pb.baseTypes) + len(*tp))
112-
if i < len(pb.baseTypes) {
113-
return pb.baseTypes[i]
114-
} else {
115-
return MakeTypeParam((*tp)[i-len(pb.baseTypes)])
116-
}
117-
} else {
118-
return pb.baseTypes[rand.Intn(len(pb.baseTypes))]
119-
}
120-
}
121-
122-
func (pb PackageBuilder) RandStructType() StructType {
123-
st := StructType{"ST", []Type{}, []string{}}
124-
for i := 0; i < pb.rs.Intn(6); i++ {
125-
t := pb.RandType()
126-
st.Ftypes = append(st.Ftypes, t)
127-
st.Fnames = append(st.Fnames, Ident(t)+strconv.Itoa(i))
128-
}
129-
return st
130-
}
131-
132-
func (pb PackageBuilder) RandFuncType() FuncType {
133-
args := make([]Type, 0, pb.rs.Intn(8))
134-
135-
// arguments
136-
for i := 0; i < cap(args); i++ {
137-
args = append(args, pb.RandType())
138-
}
139-
140-
// optionally make the last parameter variadic
141-
if len(args) > 0 && pb.rs.Intn(4) == 0 {
142-
args[len(args)-1] = EllipsisType{Base: args[len(args)-1]}
143-
}
144-
145-
// return type
146-
ret := []Type{pb.RandType()}
147-
148-
return FuncType{"FU", args, ret, true}
149-
}
1+
package microsmith
2+
3+
import (
4+
"math/rand"
5+
"strconv"
6+
)
7+
8+
// Context holds all the contextual information needed while
9+
// generating a random package.
10+
type Context struct { // TODO(alb): rename to PackageContext
11+
12+
// program-wide settings for the fuzzer
13+
programConf ProgramConf
14+
15+
// all the Costraints available declared in the package
16+
constraints []Constraint
17+
18+
// package-wide scope of vars and func available to the code in a
19+
// given moment
20+
scope *Scope
21+
22+
// Function-level scope of the type parameters available to the
23+
// code in the body. Reset and re-filled in when declaring a new
24+
// function.
25+
typeparams *Scope
26+
}
27+
28+
func NewContext(pc ProgramConf) *Context {
29+
return &Context{
30+
programConf: pc,
31+
}
32+
}
33+
34+
// ProgramConf holds program-wide configuration settings that change
35+
// the kind of programs that are generated.
36+
type ProgramConf struct {
37+
MultiPkg bool // for -multipkg
38+
TypeParams bool // for -tp
39+
}
40+
41+
// --------------------------------
42+
// Randomizers
43+
// --------------------------------
44+
45+
func RandItem[T any](r *rand.Rand, a []T) T {
46+
if r == nil {
47+
panic("RandItem called with nil rand.Rand")
48+
}
49+
return a[r.Intn(len(a))]
50+
}
51+
52+
// --------------------------------
53+
// Types Randomizers
54+
// --------------------------------
55+
56+
// Returns a slice of n random types, including composite types
57+
// (structs, array, maps, chans).
58+
func (pb PackageBuilder) RandTypes(n int) []Type {
59+
types := make([]Type, n)
60+
for i := 0; i < n; i++ {
61+
types[i] = pb.RandType()
62+
}
63+
return types
64+
}
65+
66+
// Returns a single random type (including structs, array, maps,
67+
// chans).
68+
func (pb PackageBuilder) RandType() Type {
69+
pb.typedepth++
70+
defer func() { pb.typedepth-- }()
71+
72+
if pb.typedepth >= 5 {
73+
return pb.RandBaseType()
74+
}
75+
76+
switch pb.rs.Intn(15) {
77+
case 0, 1:
78+
return ArrayOf(pb.RandType())
79+
case 2:
80+
return ChanOf(pb.RandType())
81+
case 3, 4:
82+
return MapOf(
83+
pb.RandAddressableType(),
84+
pb.RandType(),
85+
)
86+
case 5, 6:
87+
return PointerOf(pb.RandType())
88+
case 7, 8:
89+
return pb.RandStructType()
90+
case 9:
91+
return pb.RandFuncType()
92+
default:
93+
return pb.RandBaseType()
94+
}
95+
}
96+
97+
func (pb PackageBuilder) RandAddressableType() Type {
98+
types := make([]Type, 0, 32)
99+
100+
// collect addressable Base Types
101+
for _, t := range pb.baseTypes {
102+
if t.Addressable() {
103+
types = append(types, t)
104+
}
105+
}
106+
107+
// look for addressable type parameters
108+
if tp := pb.ctx.typeparams; tp != nil {
109+
for _, v := range tp.vars {
110+
if v.Type.Addressable() {
111+
types = append(types, MakeTypeParam(v))
112+
}
113+
}
114+
}
115+
116+
return RandItem(pb.rs, types)
117+
}
118+
119+
// Returns a single BaseType (primitives, or a type parameter).
120+
func (pb PackageBuilder) RandBaseType() Type {
121+
if tp := pb.ctx.typeparams; tp != nil {
122+
i := pb.rs.Intn(len(pb.baseTypes) + len(tp.vars))
123+
if i < len(pb.baseTypes) {
124+
return pb.baseTypes[i]
125+
} else {
126+
return MakeTypeParam((tp.vars)[i-len(pb.baseTypes)])
127+
}
128+
} else {
129+
return RandItem(pb.rs, pb.baseTypes)
130+
}
131+
}
132+
133+
func (pb PackageBuilder) RandStructType() StructType {
134+
st := StructType{"ST", []Type{}, []string{}}
135+
for i := 0; i < pb.rs.Intn(6); i++ {
136+
t := pb.RandType()
137+
st.Ftypes = append(st.Ftypes, t)
138+
st.Fnames = append(st.Fnames, Ident(t)+strconv.Itoa(i))
139+
}
140+
return st
141+
}
142+
143+
func (pb PackageBuilder) RandFuncType() FuncType {
144+
args := make([]Type, 0, pb.rs.Intn(8))
145+
146+
// arguments
147+
for i := 0; i < cap(args); i++ {
148+
args = append(args, pb.RandType())
149+
}
150+
151+
// optionally make the last parameter variadic
152+
if len(args) > 0 && pb.rs.Intn(4) == 0 {
153+
args[len(args)-1] = EllipsisType{Base: args[len(args)-1]}
154+
}
155+
156+
// return type
157+
ret := []Type{pb.RandType()}
158+
159+
return FuncType{"FU", args, ret, true}
160+
}

microsmith/expr.go

+8-12
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,6 @@ func (eb *ExprBuilder) Deepen() bool {
4040
return (eb.depth <= 6) && (eb.pb.rs.Float64() < 0.7)
4141
}
4242

43-
func (eb *ExprBuilder) chooseToken(tokens []token.Token) token.Token {
44-
return tokens[eb.pb.rs.Intn(len(tokens))]
45-
}
46-
4743
func (eb *ExprBuilder) Const() *ast.BasicLit {
4844
// TODO(alb): generalize
4945
return &ast.BasicLit{Kind: token.INT, Value: "77"}
@@ -172,8 +168,8 @@ func (eb *ExprBuilder) Expr(t Type) ast.Expr {
172168
case PointerType:
173169
// Either return a literal of the requested pointer type, &x
174170
// with x of type t.Base(), or nil.
175-
vt, typeInScope := eb.S().GetRandomVarOfType(t, eb.pb.rs)
176-
vst, baseInScope := eb.S().GetRandomVarOfType(t.Base(), eb.pb.rs)
171+
vt, typeInScope := eb.S().GetRandomVarOfType(t)
172+
vst, baseInScope := eb.S().GetRandomVarOfType(t.Base())
177173
if typeInScope && baseInScope {
178174
if eb.pb.rs.Intn(2) == 0 {
179175
return vt.Name
@@ -221,7 +217,7 @@ func (eb *ExprBuilder) Expr(t Type) ast.Expr {
221217

222218
func (eb *ExprBuilder) VarOrLit(t Type) ast.Expr {
223219

224-
vst, typeCanDerive := eb.S().RandVarSubType(t, eb.pb.rs)
220+
vst, typeCanDerive := eb.S().RandVarSubType(t)
225221

226222
if !typeCanDerive || !eb.Deepen() {
227223
switch t := t.(type) {
@@ -349,7 +345,7 @@ func (eb *ExprBuilder) SliceExpr(v Variable) *ast.SliceExpr {
349345
}
350346

351347
var low, high ast.Expr
352-
indV, hasInt := eb.S().GetRandomVarOfType(BasicType{"int"}, eb.pb.rs)
348+
indV, hasInt := eb.S().GetRandomVarOfType(BasicType{"int"})
353349
if hasInt && eb.Deepen() {
354350
if eb.pb.rs.Intn(8) > 0 {
355351
low = &ast.BinaryExpr{
@@ -403,7 +399,7 @@ func (eb *ExprBuilder) UnaryExpr(t Type) ast.Expr {
403399
}
404400

405401
if ops := UnaryOps(t); len(ops) > 0 {
406-
ue.Op = eb.chooseToken(ops)
402+
ue.Op = RandItem(eb.pb.rs, ops)
407403
} else {
408404
return eb.VarOrLit(t)
409405
}
@@ -433,7 +429,7 @@ func (eb *ExprBuilder) BinaryExpr(t Type) ast.Expr {
433429
}
434430
}
435431
if len(ops) > 0 {
436-
ue.Op = eb.chooseToken(ops)
432+
ue.Op = RandItem(eb.pb.rs, ops)
437433
} else {
438434
return eb.VarOrLit(t)
439435
}
@@ -458,10 +454,10 @@ func (eb *ExprBuilder) BinaryExpr(t Type) ast.Expr {
458454
}
459455

460456
// make sure the RHS is not a constant expression
461-
if vi, ok := eb.S().RandVarSubType(t2, eb.pb.rs); ok {
457+
if vi, ok := eb.S().RandVarSubType(t2); ok {
462458
ue.Y = eb.SubTypeExpr(vi.Name, vi.Type, t2)
463459
} else { // otherwise, cast from an int
464-
vi, ok := eb.S().GetRandomVarOfType(BasicType{"int"}, eb.pb.rs)
460+
vi, ok := eb.S().GetRandomVarOfType(BasicType{"int"})
465461
if !ok {
466462
panic("BinaryExpr: no int in scope")
467463
}

0 commit comments

Comments
 (0)