Beispiel #1
0
func (a *Arg) Size(typ sys.Type) uintptr {
	switch typ1 := typ.(type) {
	case sys.IntType, sys.LenType, sys.FlagsType, sys.ConstType, sys.StrConstType,
		sys.FileoffType, sys.ResourceType, sys.VmaType, sys.PtrType:
		return typ.Size()
	case sys.FilenameType:
		return uintptr(len(a.Data))
	case sys.BufferType:
		return uintptr(len(a.Data))
	case sys.StructType:
		var size uintptr
		for i, f := range typ1.Fields {
			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 {
			size += in.Size(typ1.Type)
		}
		return size
	default:
		panic("unknown arg type")
	}
}
Beispiel #2
0
// Returns inner arg for PtrType args
func (a *Arg) InnerArg(typ sys.Type) *Arg {
	switch typ1 := typ.(type) {
	case sys.PtrType:
		if a.Res == nil {
			if typ.Optional() {
				return nil
			} else {
				panic(fmt.Sprintf("non-optional pointer is nil\narg: %+v\ntype: %+v", a, typ1))
			}
		} else {
			return a.Res.InnerArg(typ1.Type)
		}
	default:
		return a
	}
}
Beispiel #3
0
func isSpecialStruct(typ sys.Type) func(r *randGen, s *state) (*Arg, []*Call) {
	a, ok := typ.(*sys.StructType)
	if !ok {
		panic("must be a struct")
	}
	switch typ.Name() {
	case "timespec":
		return func(r *randGen, s *state) (*Arg, []*Call) {
			return r.timespec(s, a, false)
		}
	case "timeval":
		return func(r *randGen, s *state) (*Arg, []*Call) {
			return r.timespec(s, a, true)
		}
	}
	return nil
}
Beispiel #4
0
func isSpecialStruct(typ sys.Type) func(r *randGen, s *state) (*Arg, []*Call) {
	if _, ok := typ.(*sys.StructType); !ok {
		panic("must be a struct")
	}
	switch typ.Name() {
	case "timespec":
		return func(r *randGen, s *state) (*Arg, []*Call) {
			return r.timespec(s, false)
		}
	case "timeval":
		return func(r *randGen, s *state) (*Arg, []*Call) {
			return r.timespec(s, true)
		}
	case "in6_addr":
		return func(r *randGen, s *state) (*Arg, []*Call) {
			return r.in6addr(s)
		}
	case "in_addr_any":
		return func(r *randGen, s *state) (*Arg, []*Call) {
			return r.inaddrany(s)
		}
	}
	return nil
}
Beispiel #5
0
func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir) (arg *Arg, calls []*Call) {
	if dir == DirOut {
		// No need to generate something interesting for output scalar arguments.
		// But we still need to generate the argument itself so that it can be referenced
		// in subsequent calls. For the same reason we do generate pointer/array/struct
		// output arguments (their elements can be referenced in subsequent calls).
		switch typ.(type) {
		case sys.IntType, sys.FlagsType, sys.ConstType, sys.StrConstType, sys.FileoffType, sys.ResourceType:
			return constArg(0), nil
		}
	}

	if typ.Optional() && r.oneOf(5) {
		if _, ok := typ.(sys.BufferType); ok {
			panic("impossible") // parent PtrType must be Optional instead
		}
		return constArg(typ.Default()), nil
	}

	switch a := typ.(type) {
	case sys.ResourceType:
		r.choose(
			1, func() {
				special := a.SpecialValues()
				arg = constArg(special[r.Intn(len(special))])
			},
			90, func() {
				// Get an existing resource.
				var allres []*Arg
				for name1, res1 := range s.resources {
					if sys.IsCompatibleResource(a.Desc.Name, name1) ||
						r.oneOf(20) && sys.IsCompatibleResource(a.Desc.Kind[0], name1) {
						allres = append(allres, res1...)
					}
				}
				if len(allres) != 0 {
					arg = resultArg(allres[r.Intn(len(allres))])
				} else {
					arg, calls = r.createResource(s, a)
				}
			},
			5, func() {
				// Create a new resource.
				arg, calls = r.createResource(s, a)
			},
		)
		return arg, calls
	case sys.FileoffType:
		// TODO: can do better
		var arg *Arg
		r.choose(
			90, func() { arg = constArg(0) },
			10, func() { arg = constArg(r.rand(100)) },
			1, func() { arg = constArg(r.randInt()) },
		)
		return arg, nil
	case sys.BufferType:
		switch a.Kind {
		case sys.BufferBlobRand, sys.BufferBlobRange:
			sz := r.randBufLen()
			if a.Kind == sys.BufferBlobRange {
				sz = r.randRange(int(a.RangeBegin), int(a.RangeEnd))
			}
			data := make([]byte, sz)
			if dir != DirOut {
				for i := range data {
					data[i] = byte(r.Intn(256))
				}
			}
			return dataArg(data), nil
		case sys.BufferString:
			data := r.randString(s)
			return dataArg(data), nil
		case sys.BufferFilesystem:
			data := r.filesystem(s)
			return dataArg(data), nil
		case sys.BufferSockaddr:
			data := r.sockaddr(s)
			if dir == DirOut {
				for i := range data {
					data[i] = 0
				}
			}
			return dataArg(data), nil
		case sys.BufferAlgType:
			data := r.algType(s)
			if dir == DirOut {
				for i := range data {
					data[i] = 0
				}
			}
			return dataArg(data), nil
		case sys.BufferAlgName:
			data := r.algName(s)
			if dir == DirOut {
				for i := range data {
					data[i] = 0
				}
			}
			return dataArg(data), nil
		default:
			panic("unknown buffer kind")
		}
	case sys.VmaType:
		npages := r.randPageCount()
		arg := r.randPageAddr(s, npages, nil, true)
		return arg, nil
	case sys.FlagsType:
		return constArg(r.flags(a.Vals)), nil
	case sys.ConstType:
		return constArg(a.Val), nil
	case sys.StrConstType:
		return dataArg([]byte(a.Val)), nil
	case sys.IntType:
		v := r.randInt()
		switch a.Kind {
		case sys.IntSignalno:
			v %= 130
		case sys.IntInaddr:
			v = uintptr(r.inaddr(s))
		case sys.IntInport:
			v = uintptr(r.inport(s))
		case sys.IntRange:
			v = r.randRangeInt(a.RangeBegin, a.RangeEnd)
		}
		return constArg(v), nil
	case sys.FilenameType:
		filename := r.filename(s)
		return dataArg([]byte(filename)), nil
	case sys.ArrayType:
		count := uintptr(0)
		switch a.Kind {
		case sys.ArrayRandLen:
			count = r.rand(6)
		case sys.ArrayRangeLen:
			count = r.randRange(int(a.RangeBegin), int(a.RangeEnd))
		}
		var inner []*Arg
		var calls []*Call
		for i := uintptr(0); i < count; i++ {
			arg1, calls1 := r.generateArg(s, a.Type, dir)
			inner = append(inner, arg1)
			calls = append(calls, calls1...)
		}
		return groupArg(inner), calls
	case *sys.StructType:
		if ctor := isSpecialStruct(a); ctor != nil && dir != DirOut {
			arg, calls = ctor(r, s)
			return
		}
		args, calls := r.generateArgs(s, a.Fields, dir)
		group := groupArg(args)
		return group, calls
	case *sys.UnionType:
		optType := a.Options[r.Intn(len(a.Options))]
		opt, calls := r.generateArg(s, optType, dir)
		return unionArg(opt, optType), calls
	case sys.PtrType:
		inner, calls := r.generateArg(s, a.Type, ArgDir(a.Dir))
		if ArgDir(a.Dir) == DirOut && inner == nil {
			// No data, but we should have got size.
			arg, calls1 := r.addr(s, inner.Size(a.Type), nil)
			calls = append(calls, calls1...)
			return arg, calls
		}
		if a.Type.Name() == "iocb" && len(s.resources["iocbptr"]) != 0 {
			// It is weird, but these are actually identified by kernel by address.
			// So try to reuse a previously used address.
			addrs := s.resources["iocbptr"]
			addr := addrs[r.Intn(len(addrs))]
			arg = pointerArg(addr.AddrPage, addr.AddrOffset, addr.AddrPagesNum, inner)
			return arg, calls
		}
		arg, calls1 := r.addr(s, inner.Size(a.Type), inner)
		calls = append(calls, calls1...)
		return arg, calls
	case sys.LenType:
		// Return placeholder value of 0 while generating len args.
		return constArg(0), nil
	default:
		panic("unknown argument type")
	}
}
Beispiel #6
0
func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir, sizes map[string]*Arg) (arg, size *Arg, calls []*Call) {
	if dir == DirOut {
		// No need to generate something interesting for output scalar arguments.
		// But we still need to generate the argument itself so that it can be referenced
		// in subsequent calls. For the same reason we do generate pointer/array/struct
		// output arguments (their elements can be referenced in subsequent calls).
		switch typ.(type) {
		case sys.IntType, sys.FlagsType, sys.ConstType, sys.StrConstType, sys.FileoffType, sys.ResourceType:
			return constArg(0), nil, nil
		}
	}

	if typ.Optional() && r.oneOf(10) {
		if _, ok := typ.(sys.BufferType); ok {
			panic("impossible") // parent PtrType must be Optional instead
		}
		return constArg(typ.Default()), constArg(0), nil
	}

	switch a := typ.(type) {
	case sys.ResourceType:
		r.choose(
			1, func() {
				special := a.SpecialValues()
				arg = constArg(special[r.Intn(len(special))])
			},
			90, func() {
				// Get an existing resource.
				if ress := s.resources[a.Kind]; ress != nil {
					allres := ress[a.Subkind]
					allres = append(allres, ress[sys.ResAny]...)
					if a.Subkind == sys.ResAny || r.oneOf(10) {
						for _, v := range ress {
							allres = append(allres, v...)
						}
					}
					if len(allres) != 0 {
						// TODO: negative PIDs mean process group,
						// we should be able to negate an existing PID.
						arg = resultArg(allres[r.Intn(len(allres))])
					}
				}
				if arg == nil {
					arg, calls = r.createResource(s, a)
				}
			},
			5, func() {
				// Create a new resource.
				arg, calls = r.createResource(s, a)
			},
		)
		return arg, nil, calls
	case sys.FileoffType:
		// TODO: can do better
		var arg *Arg
		r.choose(
			90, func() { arg = constArg(0) },
			10, func() { arg = constArg(r.rand(100)) },
			1, func() { arg = constArg(r.randInt()) },
		)
		return arg, nil, nil
	case sys.BufferType:
		switch a.Kind {
		case sys.BufferBlob:
			sz := r.randBufLen()
			if dir == DirOut {
				return nil, constArg(sz), nil
			}
			data := make([]byte, sz)
			for i := range data {
				data[i] = byte(r.Intn(256))
			}
			return dataArg(data), constArg(sz), nil
		case sys.BufferString:
			data := r.randString(s)
			return dataArg(data), constArg(uintptr(len(data))), nil
		case sys.BufferFilesystem:
			data := r.filesystem(s)
			return dataArg(data), constArg(uintptr(len(data))), nil
		case sys.BufferSockaddr:
			data := r.sockaddr(s)
			if dir == DirOut {
				return nil, constArg(uintptr(len(data))), nil
			}
			return dataArg(data), constArg(uintptr(len(data))), nil
		case sys.BufferAlgType:
			data := r.algType(s)
			if dir == DirOut {
				for i := range data {
					data[i] = 0
				}
			}
			return dataArg(data), constArg(uintptr(len(data))), nil
		case sys.BufferAlgName:
			data := r.algName(s)
			if dir == DirOut {
				for i := range data {
					data[i] = 0
				}
			}
			return dataArg(data), constArg(uintptr(len(data))), nil
		default:
			panic("unknown buffer kind")
		}
	case sys.VmaType:
		npages := r.randPageCount()
		arg := r.randPageAddr(s, npages, nil)
		return arg, pageSizeArg(npages, 0), nil
	case sys.FlagsType:
		return constArg(r.flags(a.Vals)), nil, nil
	case sys.ConstType:
		return constArg(a.Val), nil, nil
	case sys.StrConstType:
		return dataArg([]byte(a.Val)), constArg(uintptr(len(a.Val))), nil
	case sys.IntType:
		v := r.randInt()
		switch a.Kind {
		case sys.IntSignalno:
			v %= 130
		case sys.IntInaddr:
			v = uintptr(r.inaddr(s))
		case sys.IntInport:
			v = uintptr(r.inport(s))
		}
		return constArg(v), nil, nil
	case sys.FilenameType:
		filename := r.filename(s)
		return dataArg([]byte(filename)), nil, nil
	case sys.ArrayType:
		count := a.Len
		if count == 0 {
			count = r.rand(6)
		}
		var inner []*Arg
		var calls []*Call
		for i := uintptr(0); i < count; i++ {
			arg1, _, calls1 := r.generateArg(s, a.Type, dir, nil)
			inner = append(inner, arg1)
			calls = append(calls, calls1...)
		}
		return groupArg(inner), constArg(count), calls
	case sys.StructType:
		if ctor := isSpecialStruct(a); ctor != nil && dir != DirOut {
			arg, calls = ctor(r, s)
			return
		}
		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 {
			// No data, but we should have got size.
			arg, calls1 := r.addr(s, size.Val, nil)
			calls = append(calls, calls1...)
			return arg, size, calls
		}
		if size == nil {
			size = constArg(inner.Size(a.Type))
		}
		if a.Type.Name() == "iocb" && r.bin() && len(s.resources[sys.ResIocbPtr][sys.ResAny]) != 0 {
			// It is weird, but these are actually identified by kernel by address.
			// So try to reuse a previously used address.
			addrs := s.resources[sys.ResIocbPtr][sys.ResAny]
			addr := addrs[r.Intn(len(addrs))]
			arg = pointerArg(addr.AddrPage, addr.AddrOffset, inner)
			return arg, size, calls
		}
		arg, calls1 := r.addr(s, inner.Size(a.Type), inner)
		calls = append(calls, calls1...)
		return arg, size, calls
	case sys.LenType:
		if sizes == nil || sizes[a.Buf] == nil {
			fmt.Printf("name=%v buf=%v sizes=%+v\n", a.Name(), a.Buf, sizes)
			panic("me no generate len")
		}
		return sizes[a.Name()], nil, nil
	default:
		panic("unknown argument type")
	}
}
Beispiel #7
0
func parseArg(typ sys.Type, p *parser, vars map[string]*Arg) (*Arg, error) {
	r := ""
	if p.Char() == '<' {
		p.Parse('<')
		r = p.Ident()
		p.Parse('=')
		p.Parse('>')
	}
	var arg *Arg
	switch p.Char() {
	case '0':
		val := p.Ident()
		v, err := strconv.ParseUint(val, 0, 64)
		if err != nil {
			return nil, fmt.Errorf("wrong arg value '%v': %v", val, err)
		}
		arg = constArg(uintptr(v))
	case 'r':
		id := p.Ident()
		v, ok := vars[id]
		if !ok || v == nil {
			return nil, fmt.Errorf("result %v references unknown variable (vars=%+v)", id, vars)
		}
		arg = resultArg(v)
		if p.Char() == '/' {
			p.Parse('/')
			op := p.Ident()
			v, err := strconv.ParseUint(op, 0, 64)
			if err != nil {
				return nil, fmt.Errorf("wrong result div op: '%v'", op)
			}
			arg.OpDiv = uintptr(v)
		}
		if p.Char() == '+' {
			p.Parse('+')
			op := p.Ident()
			v, err := strconv.ParseUint(op, 0, 64)
			if err != nil {
				return nil, fmt.Errorf("wrong result add op: '%v'", op)
			}
			arg.OpAdd = uintptr(v)
		}
	case '&':
		var typ1 sys.Type
		switch t1 := typ.(type) {
		case sys.PtrType:
			typ1 = t1.Type
		case sys.VmaType:
		default:
			return nil, fmt.Errorf("& arg is not a pointer: %#v", typ)
		}
		p.Parse('&')
		page, off, size, err := parseAddr(p, true)
		if err != nil {
			return nil, err
		}
		p.Parse('=')
		inner, err := parseArg(typ1, p, vars)
		if err != nil {
			return nil, err
		}
		arg = pointerArg(page, off, size, inner)
	case '(':
		page, off, _, err := parseAddr(p, false)
		if err != nil {
			return nil, err
		}
		arg = pageSizeArg(page, off)
	case '"':
		p.Parse('"')
		val := ""
		if p.Char() != '"' {
			val = p.Ident()
		}
		p.Parse('"')
		data, err := hex.DecodeString(val)
		if err != nil {
			return nil, fmt.Errorf("data arg has bad value '%v'", val)
		}
		arg = dataArg(data)
	case '{':
		t1, ok := typ.(*sys.StructType)
		if !ok {
			return nil, fmt.Errorf("'{' arg is not a struct: %#v", typ)
		}
		p.Parse('{')
		var inner []*Arg
		for i := 0; p.Char() != '}'; i++ {
			if i >= len(t1.Fields) {
				return nil, fmt.Errorf("wrong struct arg count: %v, want %v", i+1, len(t1.Fields))
			}
			fld := t1.Fields[i]
			if sys.IsPad(fld) {
				inner = append(inner, constArg(0))
			} else {
				arg, err := parseArg(fld, p, vars)
				if err != nil {
					return nil, err
				}
				inner = append(inner, arg)
				if p.Char() != '}' {
					p.Parse(',')
				}
			}
		}
		p.Parse('}')
		if sys.IsPad(t1.Fields[len(t1.Fields)-1]) {
			inner = append(inner, constArg(0))
		}
		arg = groupArg(inner)
	case '[':
		t1, ok := typ.(sys.ArrayType)
		if !ok {
			return nil, fmt.Errorf("'[' arg is not an array: %#v", typ)
		}
		p.Parse('[')
		var inner []*Arg
		for i := 0; p.Char() != ']'; i++ {
			arg, err := parseArg(t1.Type, p, vars)
			if err != nil {
				return nil, err
			}
			inner = append(inner, arg)
			if p.Char() != ']' {
				p.Parse(',')
			}
		}
		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')
		p.Parse('l')
		if r != "" {
			return nil, fmt.Errorf("named nil argument")
		}
	default:
		return nil, fmt.Errorf("failed to parse argument at %v (line #%v/%v: %v)", int(p.Char()), p.l, p.i, p.s)
	}
	if r != "" {
		vars[r] = arg
	}
	return arg, nil
}
Beispiel #8
0
func returnArg(t sys.Type) *Arg {
	if t != nil {
		return &Arg{Type: t, Kind: ArgReturn, Val: t.Default()}
	}
	return &Arg{Type: t, Kind: ArgReturn}
}
Beispiel #9
0
func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir, sizes map[string]*Arg) (arg, size *Arg, calls []*Call) {
	if dir == DirOut {
		// No need to generate something interesting for output scalar arguments.
		// But we still need to generate the argument itself so that it can be referenced
		// in subsequent calls. For the same reason we do generate pointer/array/struct
		// output arguments (their elements can be referenced in subsequent calls).
		switch typ.(type) {
		case sys.IntType, sys.FlagsType, sys.FileoffType, sys.ResourceType:
			return constArg(0), nil, nil
		}
	}

	if typ.Optional() && r.oneOf(10) {
		if _, ok := typ.(sys.BufferType); ok {
			panic("impossible") // parent PtrType must be Optional instead
		}
		return constArg(typ.Default()), constArg(0), nil
	}

	switch a := typ.(type) {
	case sys.ResourceType:
		r.choose(
			1, func() {
				special := a.SpecialValues()
				arg = constArg(special[r.Intn(len(special))])
			},
			90, func() {
				// Get an existing resource.
				if ress := s.resources[a.Kind]; ress != nil {
					allres := ress[a.Subkind]
					allres = append(allres, ress[sys.ResAny]...)
					if a.Subkind == sys.ResAny || r.oneOf(10) {
						for _, v := range ress {
							allres = append(allres, v...)
						}
					}
					if len(allres) != 0 {
						// TODO: negative PIDs mean process group,
						// we should be able to negate an existing PID.
						arg = resultArg(allres[r.Intn(len(allres))])
					}
				}
				if arg == nil {
					arg, calls = r.createResource(s, a)
				}
			},
			10, func() {
				// Create a new resource.
				arg, calls = r.createResource(s, a)
			},
		)
		return arg, nil, calls
	case sys.FileoffType:
		// TODO: can do better
		var arg *Arg
		r.choose(
			90, func() { arg = constArg(0) },
			10, func() { arg = constArg(r.rand(100)) },
			1, func() { arg = constArg(r.randInt()) },
		)
		return arg, nil, nil
	case sys.BufferType:
		switch a.Kind {
		case sys.BufferBlob:
			sz := r.randBufLen()
			if dir == DirOut {
				return nil, constArg(sz), nil
			}
			data := make([]byte, sz)
			for i := range data {
				data[i] = byte(r.Intn(256))
			}
			return dataArg(data), constArg(sz), nil
		case sys.BufferString:
			data := r.randString(s)
			return dataArg(data), constArg(uintptr(len(data))), nil
		case sys.BufferSockaddr:
			data := r.sockaddr(s)
			if dir == DirOut {
				return nil, constArg(uintptr(len(data))), nil
			}
			return dataArg(data), constArg(uintptr(len(data))), nil
		default:
			panic("unknown buffer kind")
		}
	case sys.VmaType:
		npages := r.randPageCount()
		arg := r.randPageAddr(s, npages, nil)
		return arg, pageSizeArg(npages, 0), nil
	case sys.FlagsType:
		return constArg(r.flags(a.Vals)), nil, nil
	case sys.IntType:
		v := r.randInt()
		if a.Limit != 0 && !r.oneOf(100) {
			v %= a.Limit
		}
		return constArg(v), nil, nil
	case sys.FilenameType:
		filename := r.filename(s)
		return dataArg([]byte(filename)), nil, nil
	case sys.ArrayType:
		count := r.rand(6)
		var inner []*Arg
		var calls []*Call
		for i := uintptr(0); i < count; i++ {
			arg1, _, calls1 := r.generateArg(s, a.Type, dir, nil)
			inner = append(inner, arg1)
			calls = append(calls, calls1...)
		}
		return groupArg(inner), constArg(count), calls
	case sys.StructType:
		if dir != DirOut && (a.Name() == "timespec" || a.Name() == "timeval") {
			usec := a.Name() == "timeval"
			arg, calls = r.timespec(s, usec)
			return arg, nil, calls
		}
		args, calls := r.generateArgs(s, a.Fields, dir)
		return groupArg(args), nil, calls
	case sys.PtrType:
		inner, size, calls := r.generateArg(s, a.Type, ArgDir(a.Dir), sizes)
		if ArgDir(a.Dir) == DirOut && inner == nil {
			// No data, but we should have got size.
			arg, calls1 := r.addr(s, size.Val, nil)
			calls = append(calls, calls1...)
			return arg, size, calls
		}
		if size == nil {
			size = constArg(inner.Size(a.Type))
		}
		arg, calls1 := r.addr(s, inner.Size(a.Type), inner)
		calls = append(calls, calls1...)
		return arg, size, calls
	case sys.LenType:
		if sizes == nil || sizes[a.Buf] == nil {
			fmt.Printf("name=%v buf=%v sizes=%+v\n", a.Name(), a.Buf, sizes)
			panic("me no generate len")
		}
		return sizes[a.Name()], nil, nil
	default:
		panic("unknown argument type")
	}
}