diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2015-12-29 15:00:57 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2015-12-29 15:00:57 +0100 |
| commit | e6529b30ec934f285d57dc16dd8acbbab074f102 (patch) | |
| tree | 9bf9673e44997f24e702833904294f3116d4f209 /prog | |
| parent | d40104b8a35f01d31cad1f11e312e76e034ffc4a (diff) | |
sys: add union type
Diffstat (limited to 'prog')
| -rw-r--r-- | prog/analysis.go | 17 | ||||
| -rw-r--r-- | prog/clone.go | 2 | ||||
| -rw-r--r-- | prog/encoding.go | 26 | ||||
| -rw-r--r-- | prog/encodingexec.go | 9 | ||||
| -rw-r--r-- | prog/mutation.go | 10 | ||||
| -rw-r--r-- | prog/prio.go | 10 | ||||
| -rw-r--r-- | prog/prog.go | 13 | ||||
| -rw-r--r-- | prog/rand.go | 4 | ||||
| -rw-r--r-- | prog/validation.go | 20 |
9 files changed, 92 insertions, 19 deletions
diff --git a/prog/analysis.go b/prog/analysis.go index 218ab7385..dc819664e 100644 --- a/prog/analysis.go +++ b/prog/analysis.go @@ -129,6 +129,9 @@ func foreachArgArray(args *[]*Arg, ret *Arg, f func(arg, base *Arg, parent *[]*A if arg.Kind == ArgPointer && arg.Res != nil { rec(arg.Res, arg, parent) } + if arg.Kind == ArgUnion { + rec(arg.Option, base, parent) + } } for _, arg := range *args { rec(arg, nil, args) @@ -143,15 +146,14 @@ func foreachArg(c *Call, f func(arg, base *Arg, parent *[]*Arg)) { } func referencedArgs(args []*Arg, ret *Arg) (res []*Arg) { - f := func(arg, _ *Arg, _ *[]*Arg) { + foreachArgArray(&args, ret, func(arg, _ *Arg, _ *[]*Arg) { for arg1 := range arg.Uses { if arg1.Kind != ArgResult { panic("use references not ArgResult") } res = append(res, arg1) } - } - foreachArgArray(&args, ret, f) + }) return } @@ -170,10 +172,6 @@ func assignTypeAndDir(c *Call) error { case ArgPointer: arg.Dir = DirIn switch typ1 := typ.(type) { - case sys.FilenameType: - if err := rec(arg.Res, typ, dir); err != nil { - return err - } case sys.PtrType: if arg.Res != nil { if err := rec(arg.Res, typ1.Type, ArgDir(typ1.Dir)); err != nil { @@ -200,6 +198,11 @@ func assignTypeAndDir(c *Call) error { } } } + case ArgUnion: + arg.Dir = dir + if err := rec(arg.Option, arg.OptionType, dir); err != nil { + return err + } default: arg.Dir = dir } diff --git a/prog/clone.go b/prog/clone.go index dd8e6e4db..14f9db759 100644 --- a/prog/clone.go +++ b/prog/clone.go @@ -31,6 +31,8 @@ func (arg *Arg) clone(c *Call, newargs map[*Arg]*Arg) *Arg { if arg.Res != nil { arg1.Res = arg.Res.clone(c, newargs) } + case ArgUnion: + arg1.Option = arg.Option.clone(c, newargs) case ArgResult: r := newargs[arg.Res] arg1.Res = r diff --git a/prog/encoding.go b/prog/encoding.go index 47addb5ef..b9bd1ce16 100644 --- a/prog/encoding.go +++ b/prog/encoding.go @@ -109,6 +109,9 @@ func (a *Arg) serialize(buf io.Writer, vars map[*Arg]int, varSeq *int) { a1.serialize(buf, vars, varSeq) } buf.Write([]byte{delims[1]}) + case ArgUnion: + fmt.Fprintf(buf, "@%v=", a.OptionType.Name()) + a.Option.serialize(buf, vars, varSeq) default: panic("unknown arg kind") } @@ -306,6 +309,29 @@ func parseArg(typ sys.Type, p *parser, vars map[string]*Arg) (*Arg, error) { } p.Parse(']') arg = groupArg(inner) + case '@': + t1, ok := typ.(sys.UnionType) + if !ok { + return nil, fmt.Errorf("'@' arg is not a union: %#v", typ) + } + p.Parse('@') + name := p.Ident() + p.Parse('=') + var optType sys.Type + for _, t2 := range t1.Options { + if name == t2.Name() { + optType = t2 + break + } + } + if optType == nil { + return nil, fmt.Errorf("union arg %v has unknown option: %v", typ.Name(), name) + } + opt, err := parseArg(optType, p, vars) + if err != nil { + return nil, err + } + arg = unionArg(opt, optType) case 'n': p.Parse('n') p.Parse('i') diff --git a/prog/encodingexec.go b/prog/encodingexec.go index 3b68f6c98..2424a1caa 100644 --- a/prog/encodingexec.go +++ b/prog/encodingexec.go @@ -59,6 +59,10 @@ func (p *Prog) SerializeForExec() []byte { } return } + if arg1.Kind == ArgUnion { + rec(arg1.Option) + return + } if sys.IsPad(arg1.Type) { return } @@ -172,11 +176,6 @@ func (w *execContext) writeArg(arg *Arg) { } w.write(v) } - case ArgGroup: - // Squash groups. - for _, arg1 := range arg.Inner { - w.writeArg(arg1) - } default: panic("unknown arg type") } diff --git a/prog/mutation.go b/prog/mutation.go index 09cf90135..4a28a9997 100644 --- a/prog/mutation.go +++ b/prog/mutation.go @@ -154,6 +154,8 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable) { replaceArg(p, arg.Inner[i], f, calls1) calls1 = nil } + case sys.UnionType: + //!!! implement me case sys.LenType: panic("bad arg returned by mutationArgs: LenType") case sys.ConstType, sys.StrConstType: @@ -362,13 +364,11 @@ func mutationArgs(c *Call) (args, bases []*Arg, parents []*[]*Arg) { foreachArg(c, func(arg, base *Arg, parent *[]*Arg) { switch arg.Type.(type) { case sys.StructType: - switch arg.Type.Name() { - default: + if isSpecialStruct(arg.Type) == nil { // For structs only individual fields are updated. return - case "timespec", "timeval": - // These special structs are mutated as a whole. } + // These special structs are mutated as a whole. case sys.LenType: // Size is updated when the size-of arg change. return @@ -380,7 +380,7 @@ func mutationArgs(c *Call) (args, bases []*Arg, parents []*[]*Arg) { return } if base != nil { - if _, ok := base.Type.(sys.StructType); ok && (base.Type.Name() == "timespec" || base.Type.Name() == "timeval") { + if _, ok := base.Type.(sys.StructType); ok && isSpecialStruct(base.Type) != nil { // These special structs are mutated as a whole. return } diff --git a/prog/prio.go b/prog/prio.go index 2e6e46da2..d6621cb2c 100644 --- a/prog/prio.go +++ b/prog/prio.go @@ -80,6 +80,12 @@ func calcStaticPriorities() [][]float32 { if _, ok := a.Type.(sys.StructType); ok { noteUsage(1.0, "ptrto-%v", a.Type.Name()) } + if _, ok := a.Type.(sys.UnionType); ok { + noteUsage(1.0, "ptrto-%v", a.Type.Name()) + } + if arr, ok := a.Type.(sys.ArrayType); ok { + noteUsage(1.0, "ptrto-%v", arr.Type.Name()) + } case sys.BufferType: switch a.Kind { case sys.BufferBlob, sys.BufferFilesystem, sys.BufferAlgType, sys.BufferAlgName: @@ -207,6 +213,10 @@ func foreachArgType(meta *sys.Call, f func(sys.Type, ArgDir)) { for _, f := range a.Fields { rec(f, d) } + case sys.UnionType: + for _, opt := range a.Options { + rec(opt, d) + } case sys.ResourceType, sys.FileoffType, sys.BufferType, sys.VmaType, sys.LenType, sys.FlagsType, sys.ConstType, sys.StrConstType, sys.IntType, sys.FilenameType: diff --git a/prog/prog.go b/prog/prog.go index e4c1c72ca..e9e3f3d71 100644 --- a/prog/prog.go +++ b/prog/prog.go @@ -31,6 +31,10 @@ type Arg struct { Uses map[*Arg]bool // this arg is used by those ArgResult args OpDiv uintptr // divide result for ArgResult (executed before OpAdd) OpAdd uintptr // add to result for ArgResult + + // ArgUnion/UnionType + Option *Arg + OptionType sys.Type } type ArgKind int @@ -41,7 +45,8 @@ const ( ArgPointer // even if these are always constant (for reproducibility), we use a separate type because they are represented in an abstract (base+page+offset) form ArgPageSize // same as ArgPointer but base is not added, so it represents "lengths" in pages ArgData - ArgGroup // logical group of args (struct or array) + ArgGroup // logical group of args (struct or array) + ArgUnion ArgReturn // fake value denoting syscall return value ) @@ -68,6 +73,8 @@ func (a *Arg) Size(typ sys.Type) uintptr { size += a.Inner[i].Size(f) } return size + case sys.UnionType: + return a.Option.Size(a.OptionType) case sys.ArrayType: var size uintptr for _, in := range a.Inner { @@ -111,6 +118,10 @@ func groupArg(inner []*Arg) *Arg { return &Arg{Kind: ArgGroup, Inner: inner} } +func unionArg(opt *Arg, typ sys.Type) *Arg { + return &Arg{Kind: ArgUnion, Option: opt, OptionType: typ} +} + func returnArg() *Arg { return &Arg{Kind: ArgReturn, Dir: DirOut} } diff --git a/prog/rand.go b/prog/rand.go index accb6db23..f6d12a430 100644 --- a/prog/rand.go +++ b/prog/rand.go @@ -732,6 +732,10 @@ func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir, sizes map[stri } args, calls := r.generateArgs(s, a.Fields, dir) return groupArg(args), nil, calls + case sys.UnionType: + optType := a.Options[r.Intn(len(a.Options))] + opt, size, calls := r.generateArg(s, optType, dir, sizes) + return unionArg(opt, optType), size, calls case sys.PtrType: inner, size, calls := r.generateArg(s, a.Type, ArgDir(a.Dir), sizes) if ArgDir(a.Dir) == DirOut && inner == nil { diff --git a/prog/validation.go b/prog/validation.go index cc6d1ae61..4cb08c7fc 100644 --- a/prog/validation.go +++ b/prog/validation.go @@ -123,7 +123,7 @@ func (c *Call) validate(ctx *validCtx) error { switch typ1 := typ.(type) { case sys.StructType: if len(arg.Inner) != len(typ1.Fields) { - return fmt.Errorf("syscall %v: struct arg '%v' has wrong number of fields, want %v, got %v", c.Meta.Name, typ.Name(), len(typ1.Fields), len(arg.Inner)) + return fmt.Errorf("syscall %v: struct arg '%v' has wrong number of fields: want %v, got %v", c.Meta.Name, typ.Name(), len(typ1.Fields), len(arg.Inner)) } for i, arg1 := range arg.Inner { if err := checkArg(arg1, typ1.Fields[i]); err != nil { @@ -139,6 +139,24 @@ func (c *Call) validate(ctx *validCtx) error { default: return fmt.Errorf("syscall %v: group arg '%v' has bad underlying type %+v", c.Meta.Name, typ.Name(), typ) } + case ArgUnion: + typ1, ok := typ.(sys.UnionType) + if !ok { + return fmt.Errorf("syscall %v: union arg '%v' has bad type", c.Meta.Name, typ.Name()) + } + found := false + for _, typ2 := range typ1.Options { + if arg.OptionType.Name() == typ2.Name() { + found = true + break + } + } + if !found { + return fmt.Errorf("syscall %v: union arg '%v' has bad option", c.Meta.Name, typ.Name()) + } + if err := checkArg(arg.Option, arg.OptionType); err != nil { + return err + } case ArgReturn: default: return fmt.Errorf("syscall %v: unknown arg '%v' kind", c.Meta.Name, typ.Name()) |
