diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2018-01-10 16:13:34 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2018-01-13 12:52:09 +0100 |
| commit | 6b52293f4defa6b45b564d037fd641be5d6d0e0e (patch) | |
| tree | 53c7f28df3fd2666ca165ce7231d470c9d1e2dc5 /pkg/compiler/compiler.go | |
| parent | 9dc808a65eb3f44d64e078b79bcac0f0510629f6 (diff) | |
pkg/compiler: support type templates
Netlink descriptions contain tons of code duplication,
and need much more for proper descriptions. Introduce
type templates to simplify writing such descriptions
and remove code duplication.
Note: type templates are experimental, have poor error handling
and are subject to change.
Type templates can be declared as follows:
```
type buffer[DIR] ptr[DIR, array[int8]]
type fileoff[BASE] BASE
type nlattr[TYPE, PAYLOAD] {
nla_len len[parent, int16]
nla_type const[TYPE, int16]
payload PAYLOAD
} [align_4]
```
and later used as follows:
```
syscall(a buffer[in], b fileoff[int64], c ptr[in, nlattr[FOO, int32]])
```
Diffstat (limited to 'pkg/compiler/compiler.go')
| -rw-r--r-- | pkg/compiler/compiler.go | 66 |
1 files changed, 60 insertions, 6 deletions
diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index 6019bda94..98348b0f8 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -39,6 +39,8 @@ type Prog struct { StructDescs []*prog.KeyedStruct // Set of unsupported syscalls/flags. Unsupported map[string]bool + // Returned if consts was nil. + fileConsts map[string]*ConstInfo } // Compile compiles sys description. @@ -65,6 +67,20 @@ func Compile(desc *ast.Description, consts map[string]uint64, target *targets.Ta for name, typedef := range builtinTypedefs { comp.typedefs[name] = typedef } + comp.typecheck() + // The subsequent, more complex, checks expect basic validity of the tree, + // in particular corrent number of type arguments. If there were errors, + // don't proceed to avoid out-of-bounds references to type arguments. + if comp.errors != 0 { + return nil + } + if consts == nil { + fileConsts := comp.extractConsts() + if comp.errors != 0 { + return nil + } + return &Prog{fileConsts: fileConsts} + } comp.assignSyscallNumbers(consts) comp.patchConsts(consts) comp.check() @@ -177,9 +193,11 @@ func (comp *compiler) getTypeDesc(t *ast.Type) *typeDesc { func (comp *compiler) getArgsBase(t *ast.Type, field string, dir prog.Dir, isArg bool) ( *typeDesc, []*ast.Type, prog.IntTypeCommon) { desc := comp.getTypeDesc(t) + if desc == nil { + panic(fmt.Sprintf("no type desc for %#v", *t)) + } args, opt := removeOpt(t) - size := sizeUnassigned - com := genCommon(t.Ident, field, size, dir, opt) + com := genCommon(t.Ident, field, sizeUnassigned, dir, opt != nil) base := genIntCommon(com, 0, false) if desc.NeedBase { base.TypeSize = comp.ptrSize @@ -192,12 +210,48 @@ func (comp *compiler) getArgsBase(t *ast.Type, field string, dir prog.Dir, isArg return desc, args, base } -func removeOpt(t *ast.Type) ([]*ast.Type, bool) { +func (comp *compiler) foreachType(n0 ast.Node, + cb func(*ast.Type, *typeDesc, []*ast.Type, prog.IntTypeCommon)) { + switch n := n0.(type) { + case *ast.Call: + for _, arg := range n.Args { + comp.foreachSubType(arg.Type, true, cb) + } + if n.Ret != nil { + comp.foreachSubType(n.Ret, true, cb) + } + case *ast.Resource: + comp.foreachSubType(n.Base, false, cb) + case *ast.Struct: + for _, f := range n.Fields { + comp.foreachSubType(f.Type, false, cb) + } + case *ast.TypeDef: + if len(n.Args) == 0 { + comp.foreachSubType(n.Type, false, cb) + } + default: + panic(fmt.Sprintf("unexpected node %#v", n0)) + } +} + +func (comp *compiler) foreachSubType(t *ast.Type, isArg bool, + cb func(*ast.Type, *typeDesc, []*ast.Type, prog.IntTypeCommon)) { + desc, args, base := comp.getArgsBase(t, "", prog.DirIn, isArg) + cb(t, desc, args, base) + for i, arg := range args { + if desc.Args[i].Type == typeArgType { + comp.foreachSubType(arg, false, cb) + } + } +} + +func removeOpt(t *ast.Type) ([]*ast.Type, *ast.Type) { args := t.Args - if len(args) != 0 && args[len(args)-1].Ident == "opt" { - return args[:len(args)-1], true + if last := len(args) - 1; last >= 0 && args[last].Ident == "opt" { + return args[:last], args[last] } - return args, false + return args, nil } func (comp *compiler) parseIntType(name string) (size uint64, bigEndian bool) { |
