github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/internal/gosmith/context.go (about) 1 package main 2 3 /* 4 Large uncovered parts are: 5 - methods 6 - type assignability and identity 7 - consts 8 - interfaces, types implementing interfaces, type assertions 9 - ... parameters 10 */ 11 12 import ( 13 "bufio" 14 "fmt" 15 "math/rand" 16 "os" 17 "path/filepath" 18 "strings" 19 ) 20 21 type Smith struct { 22 curPackage int 23 curBlock *Block 24 curBlockPos int 25 curFunc *Func 26 27 packages [NPackages]*Package 28 29 idSeq int 30 typeDepth int 31 stmtCount int 32 exprDepth int 33 exprCount int 34 totalExprCount int 35 36 predefinedTypes []*Type 37 stringType *Type 38 boolType *Type 39 intType *Type 40 byteType *Type 41 efaceType *Type 42 runeType *Type 43 float32Type *Type 44 float64Type *Type 45 complex64Type *Type 46 complex128Type *Type 47 48 statements []func() 49 expressions []func(res *Type) string 50 51 rng *rand.Rand 52 } 53 54 const ( 55 NPackages = 3 56 NFiles = 3 57 58 NStatements = 10 59 NExprDepth = 4 60 NExprCount = 10 61 NTotalExprCount = 50 62 63 /* 64 NStatements = 30 65 NExprDepth = 6 66 NExprCount = 20 67 NTotalExprCount = 1000 68 */ 69 ) 70 71 type Package struct { 72 name string 73 imports map[string]bool 74 top *Block 75 76 undefFuncs []*Func 77 undefVars []*Var 78 79 toplevVars []*Var 80 toplevFuncs []*Func 81 } 82 83 type Block struct { 84 str string 85 parent *Block 86 subBlock *Block 87 extendable bool 88 isBreakable bool 89 isContinuable bool 90 funcBoundary bool 91 sub []*Block 92 consts []*Const 93 types []*Type 94 funcs []*Func 95 vars []*Var 96 } 97 98 type Func struct { 99 name string 100 args []*Type 101 rets []*Type 102 } 103 104 type Var struct { 105 id string 106 typ *Type 107 block *Block 108 used bool 109 } 110 111 type Const struct { 112 } 113 114 func (smith *Smith) writeProgram(dir string) { 115 smith.initTypes() 116 smith.initExpressions() 117 smith.initStatements() 118 smith.initProgram() 119 for pi := range smith.packages { 120 smith.genPackage(pi) 121 } 122 smith.serializeProgram(dir) 123 } 124 125 func (smith *Smith) initProgram() { 126 smith.packages[0] = smith.newPackage("main") 127 smith.packages[0].undefFuncs = []*Func{ 128 {name: "init", args: []*Type{}, rets: []*Type{}}, 129 {name: "init", args: []*Type{}, rets: []*Type{}}, 130 {name: "main", args: []*Type{}, rets: []*Type{}}, 131 } 132 if !*singlepkg { 133 smith.packages[1] = smith.newPackage("a") 134 smith.packages[2] = smith.newPackage("b") 135 } 136 } 137 138 func (smith *Smith) newPackage(name string) *Package { 139 return &Package{name: name, imports: make(map[string]bool), top: &Block{extendable: true}} 140 } 141 142 func (smith *Smith) genPackage(pi int) { 143 smith.typeDepth = 0 144 smith.stmtCount = 0 145 smith.exprDepth = 0 146 smith.exprCount = 0 147 smith.totalExprCount = 0 148 149 p := smith.packages[pi] 150 if p == nil { 151 return 152 } 153 for len(p.undefFuncs) != 0 || len(p.undefVars) != 0 { 154 if len(p.undefFuncs) != 0 { 155 f := p.undefFuncs[len(p.undefFuncs)-1] 156 p.undefFuncs = p.undefFuncs[:len(p.undefFuncs)-1] 157 smith.genToplevFunction(pi, f) 158 } 159 if len(p.undefVars) != 0 { 160 v := p.undefVars[len(p.undefVars)-1] 161 p.undefVars = p.undefVars[:len(p.undefVars)-1] 162 smith.genToplevVar(pi, v) 163 } 164 } 165 } 166 167 func F(f string, args ...interface{}) string { 168 return fmt.Sprintf(f, args...) 169 } 170 171 func (smith *Smith) line(f string, args ...interface{}) { 172 s := F(f, args...) 173 b := &Block{parent: smith.curBlock, str: s} 174 if smith.curBlockPos+1 == len(smith.curBlock.sub) { 175 smith.curBlock.sub = append(smith.curBlock.sub, b) 176 } else { 177 smith.curBlock.sub = append(smith.curBlock.sub, nil) 178 copy(smith.curBlock.sub[smith.curBlockPos+2:], smith.curBlock.sub[smith.curBlockPos+1:]) 179 smith.curBlock.sub[smith.curBlockPos+1] = b 180 } 181 smith.curBlockPos++ 182 } 183 184 func (smith *Smith) resetContext(pi int) { 185 smith.curPackage = pi 186 p := smith.packages[pi] 187 smith.curBlock = p.top 188 smith.curBlockPos = len(smith.curBlock.sub) - 1 189 smith.curFunc = nil 190 } 191 192 func (smith *Smith) genToplevFunction(pi int, f *Func) { 193 smith.resetContext(pi) 194 smith.curFunc = f 195 smith.enterBlock(true) 196 smith.enterBlock(true) 197 argIds := make([]string, len(f.args)) 198 argStr := "" 199 for i, a := range f.args { 200 argIds[i] = smith.newId("Param") 201 if i != 0 { 202 argStr += ", " 203 } 204 argStr += argIds[i] + " " + a.id 205 } 206 smith.line("func %v(%v)%v {", f.name, argStr, fmtTypeList(f.rets, false)) 207 for i, a := range f.args { 208 smith.defineVar(argIds[i], a) 209 } 210 smith.curBlock.funcBoundary = true 211 smith.genBlock() 212 smith.leaveBlock() 213 smith.stmtReturn() 214 smith.line("}") 215 smith.leaveBlock() 216 if f.name != "init" { 217 smith.packages[smith.curPackage].toplevFuncs = append(smith.packages[smith.curPackage].toplevFuncs, f) 218 } 219 } 220 221 func (smith *Smith) genToplevVar(pi int, v *Var) { 222 smith.resetContext(pi) 223 smith.enterBlock(true) 224 smith.line("var %v = %v", v.id, smith.rvalue(v.typ)) 225 smith.leaveBlock() 226 smith.packages[smith.curPackage].toplevVars = append(smith.packages[smith.curPackage].toplevVars, v) 227 } 228 229 func (smith *Smith) genBlock() { 230 smith.enterBlock(false) 231 for smith.rnd(10) != 0 { 232 smith.genStatement() 233 } 234 smith.leaveBlock() 235 } 236 237 func (smith *Smith) serializeProgram(dir string) { 238 for _, p := range smith.packages { 239 if p == nil { 240 continue 241 } 242 path := filepath.Join(dir, "src", p.name) 243 os.MkdirAll(path, os.ModePerm) 244 nf := NFiles 245 if *singlefile { 246 nf = 1 247 } 248 files := make([]*bufio.Writer, nf) 249 for i := range files { 250 fname := filepath.Join(path, fmt.Sprintf("%v.go", i)) 251 f, err := os.Create(fname) 252 if err != nil { 253 fmt.Fprintf(os.Stdout, "failed to create a file: %v\n", err) 254 os.Exit(1) 255 } 256 w := bufio.NewWriter(bufio.NewWriter(f)) 257 files[i] = w 258 defer func() { 259 w.Flush() 260 f.Close() 261 }() 262 fmt.Fprintf(w, "package %s\n", p.name) 263 for imp := range p.imports { 264 fmt.Fprintf(w, "import \"%s\"\n", imp) 265 } 266 if i == 0 && p.name == "main" { 267 fmt.Fprintf(w, "import \"runtime\"\n") 268 fmt.Fprintf(w, "func init() {\n") 269 fmt.Fprintf(w, " go func() {\n") 270 fmt.Fprintf(w, " for {\n") 271 fmt.Fprintf(w, " runtime.GC()\n") 272 fmt.Fprintf(w, " runtime.Gosched()\n") 273 fmt.Fprintf(w, " }\n") 274 fmt.Fprintf(w, " }()\n") 275 fmt.Fprintf(w, "}\n") 276 } 277 for imp := range p.imports { 278 fmt.Fprintf(w, "var _ = %s.UsePackage\n", imp) 279 } 280 if i == 0 { 281 fmt.Fprintf(w, "var UsePackage = 0\n") 282 fmt.Fprintf(w, "var SINK interface{}\n") 283 } 284 } 285 for _, decl := range p.top.sub { 286 serializeBlock(files[smith.rnd(len(files))], decl, 0) 287 } 288 } 289 290 path := filepath.Join(dir, "src", "a") 291 os.MkdirAll(path, os.ModePerm) 292 fname := filepath.Join(path, "0_test.go") 293 f, err := os.Create(fname) 294 if err != nil { 295 fmt.Fprintf(os.Stdout, "failed to create a file: %v\n", err) 296 os.Exit(1) 297 } 298 f.Write([]byte("package a\n")) 299 f.Close() 300 } 301 302 func serializeBlock(w *bufio.Writer, b *Block, d int) { 303 if true { 304 if b.str != "" { 305 w.WriteString(b.str) 306 w.WriteString("\n") 307 } 308 } else { 309 w.WriteString("/*" + strings.Repeat("*", d) + "*/ ") 310 w.WriteString(b.str) 311 w.WriteString(F(" // ext=%v vars=%v types=%v", b.extendable, len(b.vars), len(b.types))) 312 w.WriteString("\n") 313 } 314 for _, b1 := range b.sub { 315 serializeBlock(w, b1, d+1) 316 } 317 } 318 319 func (smith *Smith) vars() []*Var { 320 var vars []*Var 321 vars = append(vars, smith.packages[smith.curPackage].toplevVars...) 322 var f func(b *Block, pos int) 323 f = func(b *Block, pos int) { 324 for _, b1 := range b.sub[:pos+1] { 325 vars = append(vars, b1.vars...) 326 } 327 if b.parent != nil { 328 pos := len(b.parent.sub) - 1 329 if b.subBlock != nil { 330 pos = -2 331 for i, b1 := range b.parent.sub { 332 if b1 == b.subBlock { 333 pos = i 334 break 335 } 336 } 337 if pos == -2 { 338 panic("bad") 339 } 340 } 341 f(b.parent, pos) 342 } 343 } 344 f(smith.curBlock, smith.curBlockPos) 345 return vars 346 } 347 348 func (smith *Smith) types() []*Type { 349 var types []*Type 350 types = append(types, smith.predefinedTypes...) 351 var f func(b *Block, pos int) 352 f = func(b *Block, pos int) { 353 for _, b1 := range b.sub[:pos+1] { 354 types = append(types, b1.types...) 355 } 356 if b.parent != nil { 357 pos := len(b.parent.sub) - 1 358 if b.subBlock != nil { 359 pos = -2 360 for i, b1 := range b.parent.sub { 361 if b1 == b.subBlock { 362 pos = i 363 break 364 } 365 } 366 if pos == -2 { 367 panic("bad") 368 } 369 } 370 f(b.parent, pos) 371 } 372 } 373 f(smith.curBlock, smith.curBlockPos) 374 return types 375 } 376 377 func (smith *Smith) defineVar(id string, t *Type) { 378 v := &Var{id: id, typ: t, block: smith.curBlock} 379 b := smith.curBlock.sub[smith.curBlockPos] 380 b.vars = append(b.vars, v) 381 } 382 383 func (smith *Smith) defineType(t *Type) { 384 b := smith.curBlock.sub[smith.curBlockPos] 385 b.types = append(b.types, t) 386 } 387 388 func (smith *Smith) materializeVar(t *Type) string { 389 // TODO: generate var in another package 390 id := smith.newId("Var") 391 curBlock0 := smith.curBlock 392 curBlockPos0 := smith.curBlockPos 393 curBlockLen0 := len(smith.curBlock.sub) 394 exprDepth0 := smith.exprDepth 395 exprCount0 := smith.exprCount 396 smith.exprDepth = 0 397 smith.exprCount = 0 398 defer func() { 399 if smith.curBlock == curBlock0 { 400 curBlockPos0 += len(smith.curBlock.sub) - curBlockLen0 401 } 402 smith.curBlock = curBlock0 403 smith.curBlockPos = curBlockPos0 404 smith.exprDepth = exprDepth0 405 smith.exprCount = exprCount0 406 }() 407 loop: 408 for { 409 if smith.curBlock.parent == nil { 410 break 411 } 412 if !smith.curBlock.extendable || smith.curBlockPos < 0 { 413 if smith.curBlock.subBlock == nil { 414 smith.curBlockPos = len(smith.curBlock.parent.sub) - 2 415 } else { 416 smith.curBlockPos = -2 417 for i, b1 := range smith.curBlock.parent.sub { 418 if b1 == smith.curBlock.subBlock { 419 smith.curBlockPos = i 420 break 421 } 422 } 423 if smith.curBlockPos == -2 { 424 panic("bad") 425 } 426 } 427 smith.curBlock = smith.curBlock.parent 428 continue 429 } 430 if smith.rnd(3) == 0 { 431 break 432 } 433 if smith.curBlockPos >= 0 { 434 b := smith.curBlock.sub[smith.curBlockPos] 435 for _, t1 := range b.types { 436 if dependsOn(t, t1) { 437 break loop 438 } 439 } 440 } 441 smith.curBlockPos-- 442 } 443 if smith.curBlock.parent == nil { 444 for i := smith.curPackage; i < NPackages; i++ { 445 if smith.rndBool() || i == NPackages-1 || *singlepkg { 446 if i == smith.curPackage { 447 // emit global var into the current package 448 smith.enterBlock(true) 449 smith.line("var %v = %v", id, smith.rvalue(t)) 450 smith.packages[smith.curPackage].toplevVars = append(smith.packages[smith.curPackage].toplevVars, &Var{id: id, typ: t}) 451 smith.leaveBlock() 452 } else { 453 // emit global var into another package 454 smith.packages[i].undefVars = append(smith.packages[i].undefVars, &Var{id: id, typ: t}) 455 smith.packages[smith.curPackage].imports[smith.packages[i].name] = true 456 id = smith.packages[i].name + "." + id 457 } 458 break 459 } 460 } 461 } else { 462 // emit local var 463 smith.line("%v := %v", id, smith.rvalue(t)) 464 smith.defineVar(id, t) 465 } 466 return id 467 } 468 469 func (smith *Smith) materializeFunc(rets []*Type) *Func { 470 f := &Func{name: smith.newId("Func"), args: smith.atypeList(TraitGlobal), rets: rets} 471 472 curBlock0 := smith.curBlock 473 curBlockPos0 := smith.curBlockPos 474 curFunc0 := smith.curFunc 475 exprDepth0 := smith.exprDepth 476 exprCount0 := smith.exprCount 477 smith.exprDepth = 0 478 smith.exprCount = 0 479 defer func() { 480 smith.curBlock = curBlock0 481 smith.curBlockPos = curBlockPos0 482 smith.curFunc = curFunc0 483 smith.exprDepth = exprDepth0 484 smith.exprCount = exprCount0 485 }() 486 487 if smith.rndBool() && !*singlepkg && smith.curPackage != NPackages-1 { 488 for _, r1 := range rets { 489 if dependsOn(r1, nil) { 490 goto thisPackage 491 } 492 } 493 for _, t := range f.args { 494 if dependsOn(t, nil) { 495 goto thisPackage 496 } 497 } 498 // emit global var into another package 499 newF := new(Func) 500 *newF = *f 501 smith.packages[smith.curPackage+1].undefFuncs = append(smith.packages[smith.curPackage+1].undefFuncs, newF) 502 smith.packages[smith.curPackage].imports[smith.packages[smith.curPackage+1].name] = true 503 f.name = smith.packages[smith.curPackage+1].name + "." + f.name 504 return f 505 } 506 thisPackage: 507 smith.genToplevFunction(smith.curPackage, f) 508 return f 509 } 510 511 func (smith *Smith) materializeGotoLabel() string { 512 // TODO: move label up 513 id := smith.newId("Label") 514 515 curBlock0 := smith.curBlock 516 curBlockPos0 := smith.curBlockPos 517 curBlockLen0 := len(smith.curBlock.sub) 518 defer func() { 519 if smith.curBlock == curBlock0 { 520 curBlockPos0 += len(smith.curBlock.sub) - curBlockLen0 521 } 522 smith.curBlock = curBlock0 523 smith.curBlockPos = curBlockPos0 524 }() 525 526 for { 527 if smith.curBlock.parent.funcBoundary && smith.curBlockPos <= 0 { 528 break 529 } 530 if !smith.curBlock.extendable || smith.curBlockPos < 0 { 531 if smith.curBlock.subBlock != nil { 532 // we should have been stopped at func boundary 533 panic("bad") 534 } 535 smith.curBlock = smith.curBlock.parent 536 smith.curBlockPos = len(smith.curBlock.sub) - 2 537 continue 538 } 539 if smith.rnd(3) == 0 { 540 break 541 } 542 smith.curBlockPos-- 543 } 544 545 smith.line("%v:", id) 546 return id 547 } 548 549 func (smith *Smith) rnd(n int) int { 550 return smith.rng.Intn(n) 551 } 552 553 func (smith *Smith) rndBool() bool { 554 return smith.rnd(2) == 0 555 } 556 557 func (smith *Smith) choice(ch ...string) string { 558 return ch[smith.rnd(len(ch))] 559 } 560 561 func (smith *Smith) newId(prefix string) string { 562 if prefix[0] < 'A' || prefix[0] > 'Z' { 563 panic("unexported id") 564 } 565 smith.idSeq++ 566 return fmt.Sprintf("%v%v", prefix, smith.idSeq) 567 } 568 569 func (smith *Smith) enterBlock(nonextendable bool) { 570 b := &Block{parent: smith.curBlock, extendable: !nonextendable} 571 b.isBreakable = smith.curBlock.isBreakable 572 b.isContinuable = smith.curBlock.isContinuable 573 smith.curBlock.sub = append(smith.curBlock.sub, b) 574 smith.curBlock = b 575 smith.curBlockPos = -1 576 } 577 578 func (smith *Smith) leaveBlock() { 579 for _, b := range smith.curBlock.sub { 580 for _, v := range b.vars { 581 if !v.used { 582 smith.line("_ = %v", v.id) 583 } 584 } 585 } 586 587 smith.curBlock = smith.curBlock.parent 588 smith.curBlockPos = len(smith.curBlock.sub) - 1 589 }