diff options
Diffstat (limited to 'vendor/github.com/mmcloughlin/avo/gotypes/components.go')
-rw-r--r-- | vendor/github.com/mmcloughlin/avo/gotypes/components.go | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/vendor/github.com/mmcloughlin/avo/gotypes/components.go b/vendor/github.com/mmcloughlin/avo/gotypes/components.go new file mode 100644 index 0000000..2206afa --- /dev/null +++ b/vendor/github.com/mmcloughlin/avo/gotypes/components.go @@ -0,0 +1,253 @@ +package gotypes + +import ( + "errors" + "fmt" + "go/token" + "go/types" + "strconv" + + "github.com/mmcloughlin/avo/reg" + + "github.com/mmcloughlin/avo/operand" +) + +// Sizes provides type sizes used by the standard Go compiler on amd64. +var Sizes = types.SizesFor("gc", "amd64") + +// Basic represents a primitive/basic type at a given memory address. +type Basic struct { + Addr operand.Mem + Type *types.Basic +} + +// Component provides access to sub-components of a Go type. +type Component interface { + // When the component has no further sub-components, Resolve will return a + // reference to the components type and memory address. If there was an error + // during any previous calls to Component methods, they will be returned at + // resolution time. + Resolve() (*Basic, error) + + Dereference(r reg.Register) Component // dereference a pointer + Base() Component // base pointer of a string or slice + Len() Component // length of a string or slice + Cap() Component // capacity of a slice + Real() Component // real part of a complex value + Imag() Component // imaginary part of a complex value + Index(int) Component // index into an array + Field(string) Component // access a struct field +} + +// componenterr is an error that also provides a null implementation of the +// Component interface. This enables us to return an error from Component +// methods whilst also allowing method chaining to continue. +type componenterr string + +func errorf(format string, args ...interface{}) Component { + return componenterr(fmt.Sprintf(format, args...)) +} + +func (c componenterr) Error() string { return string(c) } +func (c componenterr) Resolve() (*Basic, error) { return nil, c } +func (c componenterr) Dereference(r reg.Register) Component { return c } +func (c componenterr) Base() Component { return c } +func (c componenterr) Len() Component { return c } +func (c componenterr) Cap() Component { return c } +func (c componenterr) Real() Component { return c } +func (c componenterr) Imag() Component { return c } +func (c componenterr) Index(int) Component { return c } +func (c componenterr) Field(string) Component { return c } + +type component struct { + typ types.Type + addr operand.Mem +} + +// NewComponent builds a component for the named type at the given address. +func NewComponent(t types.Type, addr operand.Mem) Component { + return &component{ + typ: t, + addr: addr, + } +} + +func (c *component) Resolve() (*Basic, error) { + b := toprimitive(c.typ) + if b == nil { + return nil, errors.New("component is not primitive") + } + return &Basic{ + Addr: c.addr, + Type: b, + }, nil +} + +func (c *component) Dereference(r reg.Register) Component { + p, ok := c.typ.Underlying().(*types.Pointer) + if !ok { + return errorf("not pointer type") + } + return NewComponent(p.Elem(), operand.Mem{Base: r}) +} + +// Reference: https://github.com/golang/go/blob/50bd1c4d4eb4fac8ddeb5f063c099daccfb71b26/src/reflect/value.go#L1800-L1804 +// +// type SliceHeader struct { +// Data uintptr +// Len int +// Cap int +// } +// +var slicehdroffsets = Sizes.Offsetsof([]*types.Var{ + types.NewField(token.NoPos, nil, "Data", types.Typ[types.Uintptr], false), + types.NewField(token.NoPos, nil, "Len", types.Typ[types.Int], false), + types.NewField(token.NoPos, nil, "Cap", types.Typ[types.Int], false), +}) + +func (c *component) Base() Component { + if !isslice(c.typ) && !isstring(c.typ) { + return errorf("only slices and strings have base pointers") + } + return c.sub("_base", int(slicehdroffsets[0]), types.Typ[types.Uintptr]) +} + +func (c *component) Len() Component { + if !isslice(c.typ) && !isstring(c.typ) { + return errorf("only slices and strings have length fields") + } + return c.sub("_len", int(slicehdroffsets[1]), types.Typ[types.Int]) +} + +func (c *component) Cap() Component { + if !isslice(c.typ) { + return errorf("only slices have capacity fields") + } + return c.sub("_cap", int(slicehdroffsets[2]), types.Typ[types.Int]) +} + +func (c *component) Real() Component { + if !iscomplex(c.typ) { + return errorf("only complex types have real values") + } + f := complextofloat(c.typ) + return c.sub("_real", 0, f) +} + +func (c *component) Imag() Component { + if !iscomplex(c.typ) { + return errorf("only complex types have imaginary values") + } + f := complextofloat(c.typ) + return c.sub("_imag", int(Sizes.Sizeof(f)), f) +} + +func (c *component) Index(i int) Component { + a, ok := c.typ.Underlying().(*types.Array) + if !ok { + return errorf("not array type") + } + if int64(i) >= a.Len() { + return errorf("array index out of bounds") + } + // Reference: https://github.com/golang/tools/blob/bcd4e47d02889ebbc25c9f4bf3d27e4124b0bf9d/go/analysis/passes/asmdecl/asmdecl.go#L482-L494 + // + // case asmArray: + // tu := t.Underlying().(*types.Array) + // elem := tu.Elem() + // // Calculate offset of each element array. + // fields := []*types.Var{ + // types.NewVar(token.NoPos, nil, "fake0", elem), + // types.NewVar(token.NoPos, nil, "fake1", elem), + // } + // offsets := arch.sizes.Offsetsof(fields) + // elemoff := int(offsets[1]) + // for i := 0; i < int(tu.Len()); i++ { + // cc = appendComponentsRecursive(arch, elem, cc, suffix+"_"+strconv.Itoa(i), i*elemoff) + // } + // + elem := a.Elem() + elemsize := int(Sizes.Sizeof(types.NewArray(elem, 2)) - Sizes.Sizeof(types.NewArray(elem, 1))) + return c.sub("_"+strconv.Itoa(i), i*elemsize, elem) +} + +func (c *component) Field(n string) Component { + s, ok := c.typ.Underlying().(*types.Struct) + if !ok { + return errorf("not struct type") + } + // Reference: https://github.com/golang/tools/blob/13ba8ad772dfbf0f451b5dd0679e9c5605afc05d/go/analysis/passes/asmdecl/asmdecl.go#L471-L480 + // + // case asmStruct: + // tu := t.Underlying().(*types.Struct) + // fields := make([]*types.Var, tu.NumFields()) + // for i := 0; i < tu.NumFields(); i++ { + // fields[i] = tu.Field(i) + // } + // offsets := arch.sizes.Offsetsof(fields) + // for i, f := range fields { + // cc = appendComponentsRecursive(arch, f.Type(), cc, suffix+"_"+f.Name(), off+int(offsets[i])) + // } + // + fields := make([]*types.Var, s.NumFields()) + for i := 0; i < s.NumFields(); i++ { + fields[i] = s.Field(i) + } + offsets := Sizes.Offsetsof(fields) + for i, f := range fields { + if f.Name() == n { + return c.sub("_"+n, int(offsets[i]), f.Type()) + } + } + return errorf("struct does not have field '%s'", n) +} + +func (c *component) sub(suffix string, offset int, t types.Type) *component { + s := *c + if s.addr.Symbol.Name != "" { + s.addr.Symbol.Name += suffix + } + s.addr = s.addr.Offset(offset) + s.typ = t + return &s +} + +func isslice(t types.Type) bool { + _, ok := t.Underlying().(*types.Slice) + return ok +} + +func isstring(t types.Type) bool { + b, ok := t.Underlying().(*types.Basic) + return ok && b.Kind() == types.String +} + +func iscomplex(t types.Type) bool { + b, ok := t.Underlying().(*types.Basic) + return ok && (b.Info()&types.IsComplex) != 0 +} + +func complextofloat(t types.Type) types.Type { + switch Sizes.Sizeof(t) { + case 16: + return types.Typ[types.Float64] + case 8: + return types.Typ[types.Float32] + } + panic("bad") +} + +// toprimitive determines whether t is primitive (cannot be reduced into +// components). If it is, it returns the basic type for t, otherwise returns +// nil. +func toprimitive(t types.Type) *types.Basic { + switch b := t.(type) { + case *types.Basic: + if (b.Info() & (types.IsString | types.IsComplex)) == 0 { + return b + } + case *types.Pointer: + return types.Typ[types.Uintptr] + } + return nil +} |