github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/cmd/signature-fuzzer/internal/fuzz-generator/parm.go (about) 1 // Copyright 2021 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package generator 6 7 import ( 8 "bytes" 9 "fmt" 10 "os" 11 "sort" 12 ) 13 14 // parm is an interface describing an abstract parameter var or return 15 // var; there will be concrete types of various sorts that implement 16 // this interface. 17 type parm interface { 18 19 // Declare emits text containing a declaration of this param 20 // or return var into the specified buffer. Prefix is a tag to 21 // prepend before the declaration (for example a variable 22 // name) followed by a space; suffix is an arbitrary string to 23 // tack onto the end of the param's type text. Here 'caller' 24 // is set to true if we're emitting the caller part of a test 25 // pair as opposed to the checker. 26 Declare(b *bytes.Buffer, prefix string, suffix string, caller bool) 27 28 // GenElemRef returns a pair [X,Y] corresponding to a 29 // component piece of some composite parm, where X is a string 30 // forming the reference (ex: ".field" if we're picking out a 31 // struct field) and Y is a parm object corresponding to the 32 // type of the element. 33 GenElemRef(elidx int, path string) (string, parm) 34 35 // GenValue constructs a new concrete random value appropriate 36 // for the type in question and returns it, along with a 37 // sequence number indicating how many random decisions we had 38 // to make. Here "s" is the current generator state, "f" is 39 // the current function we're emitting, value is a sequence 40 // number indicating how many random decisions have been made 41 // up until this point, and 'caller' is set to true if we're 42 // emitting the caller part of a test pair as opposed to the 43 // checker. Return value is a pair [V,I] where V is the text 44 // if the value, and I is a new sequence number reflecting any 45 // additional random choices we had to make. For example, if 46 // the parm is something like "type Foo struct { f1 int32; f2 47 // float64 }" then we might expect GenValue to emit something 48 // like "Foo{int32(-9), float64(123.123)}". 49 GenValue(s *genstate, f *funcdef, value int, caller bool) (string, int) 50 51 // IsControl returns true if this specific param has been marked 52 // as the single param that controls recursion for a recursive 53 // checker function. The test code doesn't check this param for a specific 54 // value, but instead returns early if it has value 0 or decrements it 55 // on a recursive call. 56 IsControl() bool 57 58 // NumElements returns the total number of discrete elements contained 59 // in this parm. For non-composite types, this will always be 1. 60 NumElements() int 61 62 // String returns a descriptive string for this parm. 63 String() string 64 65 // TypeName returns the non-qualified type name for this parm. 66 TypeName() string 67 68 // QualName returns a package-qualified type name for this parm. 69 QualName() string 70 71 // HasPointer returns true if this parm is of pointer type, or 72 // if it is a composite that has a pointer element somewhere inside. 73 // Strings and slices return true for this hook. 74 HasPointer() bool 75 76 // IsBlank() returns true if the name of this parm is "_" (that is, 77 // if we randomly chose to make it a blank). SetBlank() is used 78 // to set the 'blank' property for this parm. 79 IsBlank() bool 80 SetBlank(v bool) 81 82 // AddrTaken() return a token indicating whether this parm should 83 // be address taken or not, the nature of the address-taken-ness (see 84 // below at the def of addrTakenHow). SetAddrTaken is used to set 85 // the address taken property of the parm. 86 AddrTaken() addrTakenHow 87 SetAddrTaken(val addrTakenHow) 88 89 // IsGenVal() returns true if the values of this type should 90 // be obtained by calling a helper func, as opposed to 91 // emitting code inline (as one would for things like numeric 92 // types). SetIsGenVal is used to set the gen-val property of 93 // the parm. 94 IsGenVal() bool 95 SetIsGenVal(val bool) 96 97 // SkipCompare() returns true if we've randomly decided that 98 // we don't want to compare the value for this param or 99 // return. SetSkipCompare is used to set the skip-compare 100 // property of the parm. 101 SkipCompare() skipCompare 102 SetSkipCompare(val skipCompare) 103 } 104 105 type addrTakenHow uint8 106 107 const ( 108 // Param not address taken. 109 notAddrTaken addrTakenHow = 0 110 111 // Param address is taken and used for simple reads/writes. 112 addrTakenSimple addrTakenHow = 1 113 114 // Param address is taken and passed to a well-behaved function. 115 addrTakenPassed addrTakenHow = 2 116 117 // Param address is taken and stored to a global var. 118 addrTakenHeap addrTakenHow = 3 119 ) 120 121 func (a *addrTakenHow) AddrTaken() addrTakenHow { 122 return *a 123 } 124 125 func (a *addrTakenHow) SetAddrTaken(val addrTakenHow) { 126 *a = val 127 } 128 129 type isBlank bool 130 131 func (b *isBlank) IsBlank() bool { 132 return bool(*b) 133 } 134 135 func (b *isBlank) SetBlank(val bool) { 136 *b = isBlank(val) 137 } 138 139 type isGenValFunc bool 140 141 func (g *isGenValFunc) IsGenVal() bool { 142 return bool(*g) 143 } 144 145 func (g *isGenValFunc) SetIsGenVal(val bool) { 146 *g = isGenValFunc(val) 147 } 148 149 type skipCompare int 150 151 const ( 152 // Param not address taken. 153 SkipAll = -1 154 SkipNone = 0 155 SkipPayload = 1 156 ) 157 158 func (s *skipCompare) SkipCompare() skipCompare { 159 return skipCompare(*s) 160 } 161 162 func (s *skipCompare) SetSkipCompare(val skipCompare) { 163 *s = skipCompare(val) 164 } 165 166 // containedParms takes an arbitrary param 'p' and returns a slice 167 // with 'p' itself plus any component parms contained within 'p'. 168 func containedParms(p parm) []parm { 169 visited := make(map[string]parm) 170 worklist := []parm{p} 171 172 addToWork := func(p parm) { 173 if p == nil { 174 panic("not expected") 175 } 176 if _, ok := visited[p.TypeName()]; !ok { 177 worklist = append(worklist, p) 178 } 179 } 180 181 for len(worklist) != 0 { 182 cp := worklist[0] 183 worklist = worklist[1:] 184 if _, ok := visited[cp.TypeName()]; ok { 185 continue 186 } 187 visited[cp.TypeName()] = cp 188 switch x := cp.(type) { 189 case *mapparm: 190 addToWork(x.keytype) 191 addToWork(x.valtype) 192 case *structparm: 193 for _, fld := range x.fields { 194 addToWork(fld) 195 } 196 case *arrayparm: 197 addToWork(x.eltype) 198 case *pointerparm: 199 addToWork(x.totype) 200 case *typedefparm: 201 addToWork(x.target) 202 } 203 } 204 rv := []parm{} 205 for _, v := range visited { 206 rv = append(rv, v) 207 } 208 sort.Slice(rv, func(i, j int) bool { 209 if rv[i].TypeName() == rv[j].TypeName() { 210 fmt.Fprintf(os.Stderr, "%d %d %+v %+v %s %s\n", i, j, rv[i], rv[i].String(), rv[j], rv[j].String()) 211 panic("unexpected") 212 } 213 return rv[i].TypeName() < rv[j].TypeName() 214 }) 215 return rv 216 }