+++ /dev/null
-package main
-
-/*
-Large uncovered parts are:
-- methods
-- type assignability and identity
-- consts
-- interfaces, types implementing interfaces, type assertions
-- ... parameters
-*/
-
-import (
- "bufio"
- "fmt"
- "math/rand"
- "os"
- "path/filepath"
- "strings"
-)
-
-type Smith struct {
- curPackage int
- curBlock *Block
- curBlockPos int
- curFunc *Func
-
- packages [NPackages]*Package
-
- idSeq int
- typeDepth int
- stmtCount int
- exprDepth int
- exprCount int
- totalExprCount int
-
- predefinedTypes []*Type
- stringType *Type
- boolType *Type
- intType *Type
- byteType *Type
- efaceType *Type
- runeType *Type
- float32Type *Type
- float64Type *Type
- complex64Type *Type
- complex128Type *Type
-
- statements []func()
- expressions []func(res *Type) string
-
- rng *rand.Rand
-}
-
-const (
- NPackages = 3
- NFiles = 3
-
- NStatements = 10
- NExprDepth = 4
- NExprCount = 10
- NTotalExprCount = 50
-
-/*
- NStatements = 30
- NExprDepth = 6
- NExprCount = 20
- NTotalExprCount = 1000
-*/
-)
-
-type Package struct {
- name string
- imports map[string]bool
- top *Block
-
- undefFuncs []*Func
- undefVars []*Var
-
- toplevVars []*Var
- toplevFuncs []*Func
-}
-
-type Block struct {
- str string
- parent *Block
- subBlock *Block
- extendable bool
- isBreakable bool
- isContinuable bool
- funcBoundary bool
- sub []*Block
- consts []*Const
- types []*Type
- funcs []*Func
- vars []*Var
-}
-
-type Func struct {
- name string
- args []*Type
- rets []*Type
-}
-
-type Var struct {
- id string
- typ *Type
- block *Block
- used bool
-}
-
-type Const struct {
-}
-
-func (smith *Smith) writeProgram(dir string) {
- smith.initTypes()
- smith.initExpressions()
- smith.initStatements()
- smith.initProgram()
- for pi := range smith.packages {
- smith.genPackage(pi)
- }
- smith.serializeProgram(dir)
-}
-
-func (smith *Smith) initProgram() {
- smith.packages[0] = smith.newPackage("main")
- smith.packages[0].undefFuncs = []*Func{
- {name: "init", args: []*Type{}, rets: []*Type{}},
- {name: "init", args: []*Type{}, rets: []*Type{}},
- {name: "main", args: []*Type{}, rets: []*Type{}},
- }
- if !*singlepkg {
- smith.packages[1] = smith.newPackage("a")
- smith.packages[2] = smith.newPackage("b")
- }
-}
-
-func (smith *Smith) newPackage(name string) *Package {
- return &Package{name: name, imports: make(map[string]bool), top: &Block{extendable: true}}
-}
-
-func (smith *Smith) genPackage(pi int) {
- smith.typeDepth = 0
- smith.stmtCount = 0
- smith.exprDepth = 0
- smith.exprCount = 0
- smith.totalExprCount = 0
-
- p := smith.packages[pi]
- if p == nil {
- return
- }
- for len(p.undefFuncs) != 0 || len(p.undefVars) != 0 {
- if len(p.undefFuncs) != 0 {
- f := p.undefFuncs[len(p.undefFuncs)-1]
- p.undefFuncs = p.undefFuncs[:len(p.undefFuncs)-1]
- smith.genToplevFunction(pi, f)
- }
- if len(p.undefVars) != 0 {
- v := p.undefVars[len(p.undefVars)-1]
- p.undefVars = p.undefVars[:len(p.undefVars)-1]
- smith.genToplevVar(pi, v)
- }
- }
-}
-
-func F(f string, args ...interface{}) string {
- return fmt.Sprintf(f, args...)
-}
-
-func (smith *Smith) line(f string, args ...interface{}) {
- s := F(f, args...)
- b := &Block{parent: smith.curBlock, str: s}
- if smith.curBlockPos+1 == len(smith.curBlock.sub) {
- smith.curBlock.sub = append(smith.curBlock.sub, b)
- } else {
- smith.curBlock.sub = append(smith.curBlock.sub, nil)
- copy(smith.curBlock.sub[smith.curBlockPos+2:], smith.curBlock.sub[smith.curBlockPos+1:])
- smith.curBlock.sub[smith.curBlockPos+1] = b
- }
- smith.curBlockPos++
-}
-
-func (smith *Smith) resetContext(pi int) {
- smith.curPackage = pi
- p := smith.packages[pi]
- smith.curBlock = p.top
- smith.curBlockPos = len(smith.curBlock.sub) - 1
- smith.curFunc = nil
-}
-
-func (smith *Smith) genToplevFunction(pi int, f *Func) {
- smith.resetContext(pi)
- smith.curFunc = f
- smith.enterBlock(true)
- smith.enterBlock(true)
- argIds := make([]string, len(f.args))
- argStr := ""
- for i, a := range f.args {
- argIds[i] = smith.newId("Param")
- if i != 0 {
- argStr += ", "
- }
- argStr += argIds[i] + " " + a.id
- }
- smith.line("func %v(%v)%v {", f.name, argStr, fmtTypeList(f.rets, false))
- for i, a := range f.args {
- smith.defineVar(argIds[i], a)
- }
- smith.curBlock.funcBoundary = true
- smith.genBlock()
- smith.leaveBlock()
- smith.stmtReturn()
- smith.line("}")
- smith.leaveBlock()
- if f.name != "init" {
- smith.packages[smith.curPackage].toplevFuncs = append(smith.packages[smith.curPackage].toplevFuncs, f)
- }
-}
-
-func (smith *Smith) genToplevVar(pi int, v *Var) {
- smith.resetContext(pi)
- smith.enterBlock(true)
- smith.line("var %v = %v", v.id, smith.rvalue(v.typ))
- smith.leaveBlock()
- smith.packages[smith.curPackage].toplevVars = append(smith.packages[smith.curPackage].toplevVars, v)
-}
-
-func (smith *Smith) genBlock() {
- smith.enterBlock(false)
- for smith.rnd(10) != 0 {
- smith.genStatement()
- }
- smith.leaveBlock()
-}
-
-func (smith *Smith) serializeProgram(dir string) {
- for _, p := range smith.packages {
- if p == nil {
- continue
- }
- path := filepath.Join(dir, "src", p.name)
- os.MkdirAll(path, os.ModePerm)
- nf := NFiles
- if *singlefile {
- nf = 1
- }
- files := make([]*bufio.Writer, nf)
- for i := range files {
- fname := filepath.Join(path, fmt.Sprintf("%v.go", i))
- f, err := os.Create(fname)
- if err != nil {
- fmt.Fprintf(os.Stdout, "failed to create a file: %v\n", err)
- os.Exit(1)
- }
- w := bufio.NewWriter(bufio.NewWriter(f))
- files[i] = w
- defer func() {
- w.Flush()
- f.Close()
- }()
- fmt.Fprintf(w, "package %s\n", p.name)
- for imp := range p.imports {
- fmt.Fprintf(w, "import \"%s\"\n", imp)
- }
- if i == 0 && p.name == "main" {
- fmt.Fprintf(w, "import \"runtime\"\n")
- fmt.Fprintf(w, "func init() {\n")
- fmt.Fprintf(w, " go func() {\n")
- fmt.Fprintf(w, " for {\n")
- fmt.Fprintf(w, " runtime.GC()\n")
- fmt.Fprintf(w, " runtime.Gosched()\n")
- fmt.Fprintf(w, " }\n")
- fmt.Fprintf(w, " }()\n")
- fmt.Fprintf(w, "}\n")
- }
- for imp := range p.imports {
- fmt.Fprintf(w, "var _ = %s.UsePackage\n", imp)
- }
- if i == 0 {
- fmt.Fprintf(w, "var UsePackage = 0\n")
- fmt.Fprintf(w, "var SINK interface{}\n")
- }
- }
- for _, decl := range p.top.sub {
- serializeBlock(files[smith.rnd(len(files))], decl, 0)
- }
- }
-
- path := filepath.Join(dir, "src", "a")
- os.MkdirAll(path, os.ModePerm)
- fname := filepath.Join(path, "0_test.go")
- f, err := os.Create(fname)
- if err != nil {
- fmt.Fprintf(os.Stdout, "failed to create a file: %v\n", err)
- os.Exit(1)
- }
- f.Write([]byte("package a\n"))
- f.Close()
-}
-
-func serializeBlock(w *bufio.Writer, b *Block, d int) {
- if true {
- if b.str != "" {
- w.WriteString(b.str)
- w.WriteString("\n")
- }
- } else {
- w.WriteString("/*" + strings.Repeat("*", d) + "*/ ")
- w.WriteString(b.str)
- w.WriteString(F(" // ext=%v vars=%v types=%v", b.extendable, len(b.vars), len(b.types)))
- w.WriteString("\n")
- }
- for _, b1 := range b.sub {
- serializeBlock(w, b1, d+1)
- }
-}
-
-func (smith *Smith) vars() []*Var {
- var vars []*Var
- vars = append(vars, smith.packages[smith.curPackage].toplevVars...)
- var f func(b *Block, pos int)
- f = func(b *Block, pos int) {
- for _, b1 := range b.sub[:pos+1] {
- vars = append(vars, b1.vars...)
- }
- if b.parent != nil {
- pos := len(b.parent.sub) - 1
- if b.subBlock != nil {
- pos = -2
- for i, b1 := range b.parent.sub {
- if b1 == b.subBlock {
- pos = i
- break
- }
- }
- if pos == -2 {
- panic("bad")
- }
- }
- f(b.parent, pos)
- }
- }
- f(smith.curBlock, smith.curBlockPos)
- return vars
-}
-
-func (smith *Smith) types() []*Type {
- var types []*Type
- types = append(types, smith.predefinedTypes...)
- var f func(b *Block, pos int)
- f = func(b *Block, pos int) {
- for _, b1 := range b.sub[:pos+1] {
- types = append(types, b1.types...)
- }
- if b.parent != nil {
- pos := len(b.parent.sub) - 1
- if b.subBlock != nil {
- pos = -2
- for i, b1 := range b.parent.sub {
- if b1 == b.subBlock {
- pos = i
- break
- }
- }
- if pos == -2 {
- panic("bad")
- }
- }
- f(b.parent, pos)
- }
- }
- f(smith.curBlock, smith.curBlockPos)
- return types
-}
-
-func (smith *Smith) defineVar(id string, t *Type) {
- v := &Var{id: id, typ: t, block: smith.curBlock}
- b := smith.curBlock.sub[smith.curBlockPos]
- b.vars = append(b.vars, v)
-}
-
-func (smith *Smith) defineType(t *Type) {
- b := smith.curBlock.sub[smith.curBlockPos]
- b.types = append(b.types, t)
-}
-
-func (smith *Smith) materializeVar(t *Type) string {
- // TODO: generate var in another package
- id := smith.newId("Var")
- curBlock0 := smith.curBlock
- curBlockPos0 := smith.curBlockPos
- curBlockLen0 := len(smith.curBlock.sub)
- exprDepth0 := smith.exprDepth
- exprCount0 := smith.exprCount
- smith.exprDepth = 0
- smith.exprCount = 0
- defer func() {
- if smith.curBlock == curBlock0 {
- curBlockPos0 += len(smith.curBlock.sub) - curBlockLen0
- }
- smith.curBlock = curBlock0
- smith.curBlockPos = curBlockPos0
- smith.exprDepth = exprDepth0
- smith.exprCount = exprCount0
- }()
-loop:
- for {
- if smith.curBlock.parent == nil {
- break
- }
- if !smith.curBlock.extendable || smith.curBlockPos < 0 {
- if smith.curBlock.subBlock == nil {
- smith.curBlockPos = len(smith.curBlock.parent.sub) - 2
- } else {
- smith.curBlockPos = -2
- for i, b1 := range smith.curBlock.parent.sub {
- if b1 == smith.curBlock.subBlock {
- smith.curBlockPos = i
- break
- }
- }
- if smith.curBlockPos == -2 {
- panic("bad")
- }
- }
- smith.curBlock = smith.curBlock.parent
- continue
- }
- if smith.rnd(3) == 0 {
- break
- }
- if smith.curBlockPos >= 0 {
- b := smith.curBlock.sub[smith.curBlockPos]
- for _, t1 := range b.types {
- if dependsOn(t, t1) {
- break loop
- }
- }
- }
- smith.curBlockPos--
- }
- if smith.curBlock.parent == nil {
- for i := smith.curPackage; i < NPackages; i++ {
- if smith.rndBool() || i == NPackages-1 || *singlepkg {
- if i == smith.curPackage {
- // emit global var into the current package
- smith.enterBlock(true)
- smith.line("var %v = %v", id, smith.rvalue(t))
- smith.packages[smith.curPackage].toplevVars = append(smith.packages[smith.curPackage].toplevVars, &Var{id: id, typ: t})
- smith.leaveBlock()
- } else {
- // emit global var into another package
- smith.packages[i].undefVars = append(smith.packages[i].undefVars, &Var{id: id, typ: t})
- smith.packages[smith.curPackage].imports[smith.packages[i].name] = true
- id = smith.packages[i].name + "." + id
- }
- break
- }
- }
- } else {
- // emit local var
- smith.line("%v := %v", id, smith.rvalue(t))
- smith.defineVar(id, t)
- }
- return id
-}
-
-func (smith *Smith) materializeFunc(rets []*Type) *Func {
- f := &Func{name: smith.newId("Func"), args: smith.atypeList(TraitGlobal), rets: rets}
-
- curBlock0 := smith.curBlock
- curBlockPos0 := smith.curBlockPos
- curFunc0 := smith.curFunc
- exprDepth0 := smith.exprDepth
- exprCount0 := smith.exprCount
- smith.exprDepth = 0
- smith.exprCount = 0
- defer func() {
- smith.curBlock = curBlock0
- smith.curBlockPos = curBlockPos0
- smith.curFunc = curFunc0
- smith.exprDepth = exprDepth0
- smith.exprCount = exprCount0
- }()
-
- if smith.rndBool() && !*singlepkg && smith.curPackage != NPackages-1 {
- for _, r1 := range rets {
- if dependsOn(r1, nil) {
- goto thisPackage
- }
- }
- for _, t := range f.args {
- if dependsOn(t, nil) {
- goto thisPackage
- }
- }
- // emit global var into another package
- newF := new(Func)
- *newF = *f
- smith.packages[smith.curPackage+1].undefFuncs = append(smith.packages[smith.curPackage+1].undefFuncs, newF)
- smith.packages[smith.curPackage].imports[smith.packages[smith.curPackage+1].name] = true
- f.name = smith.packages[smith.curPackage+1].name + "." + f.name
- return f
- }
-thisPackage:
- smith.genToplevFunction(smith.curPackage, f)
- return f
-}
-
-func (smith *Smith) materializeGotoLabel() string {
- // TODO: move lavel up
- id := smith.newId("Label")
-
- curBlock0 := smith.curBlock
- curBlockPos0 := smith.curBlockPos
- curBlockLen0 := len(smith.curBlock.sub)
- defer func() {
- if smith.curBlock == curBlock0 {
- curBlockPos0 += len(smith.curBlock.sub) - curBlockLen0
- }
- smith.curBlock = curBlock0
- smith.curBlockPos = curBlockPos0
- }()
-
- for {
- if smith.curBlock.parent.funcBoundary && smith.curBlockPos <= 0 {
- break
- }
- if !smith.curBlock.extendable || smith.curBlockPos < 0 {
- if smith.curBlock.subBlock != nil {
- // we should have been stopped at func boundary
- panic("bad")
- }
- smith.curBlock = smith.curBlock.parent
- smith.curBlockPos = len(smith.curBlock.sub) - 2
- continue
- }
- if smith.rnd(3) == 0 {
- break
- }
- smith.curBlockPos--
- }
-
- smith.line("%v:", id)
- return id
-}
-
-func (smith *Smith) rnd(n int) int {
- return smith.rng.Intn(n)
-}
-
-func (smith *Smith) rndBool() bool {
- return smith.rnd(2) == 0
-}
-
-func (smith *Smith) choice(ch ...string) string {
- return ch[smith.rnd(len(ch))]
-}
-
-func (smith *Smith) newId(prefix string) string {
- if prefix[0] < 'A' || prefix[0] > 'Z' {
- panic("unexported id")
- }
- smith.idSeq++
- return fmt.Sprintf("%v%v", prefix, smith.idSeq)
-}
-
-func (smith *Smith) enterBlock(nonextendable bool) {
- b := &Block{parent: smith.curBlock, extendable: !nonextendable}
- b.isBreakable = smith.curBlock.isBreakable
- b.isContinuable = smith.curBlock.isContinuable
- smith.curBlock.sub = append(smith.curBlock.sub, b)
- smith.curBlock = b
- smith.curBlockPos = -1
-}
-
-func (smith *Smith) leaveBlock() {
- for _, b := range smith.curBlock.sub {
- for _, v := range b.vars {
- if !v.used {
- smith.line("_ = %v", v.id)
- }
- }
- }
-
- smith.curBlock = smith.curBlock.parent
- smith.curBlockPos = len(smith.curBlock.sub) - 1
-}