1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
package gotypes
import (
"bytes"
"errors"
"fmt"
"go/token"
"go/types"
"strconv"
"github.com/mmcloughlin/avo/operand"
)
// Signature represents a Go function signature.
type Signature struct {
pkg *types.Package
sig *types.Signature
params *Tuple
results *Tuple
}
// NewSignature constructs a Signature.
func NewSignature(pkg *types.Package, sig *types.Signature) *Signature {
s := &Signature{
pkg: pkg,
sig: sig,
}
s.init()
return s
}
// NewSignatureVoid builds the void signature "func()".
func NewSignatureVoid() *Signature {
return NewSignature(nil, types.NewSignature(nil, nil, nil, false))
}
// LookupSignature returns the signature of the named function in the provided package.
func LookupSignature(pkg *types.Package, name string) (*Signature, error) {
scope := pkg.Scope()
obj := scope.Lookup(name)
if obj == nil {
return nil, fmt.Errorf("could not find function \"%s\"", name)
}
s, ok := obj.Type().(*types.Signature)
if !ok {
return nil, fmt.Errorf("object \"%s\" does not have signature type", name)
}
return NewSignature(pkg, s), nil
}
// ParseSignature builds a Signature by parsing a Go function type expression.
// The function type must reference builtin types only; see
// ParseSignatureInPackage if custom types are required.
func ParseSignature(expr string) (*Signature, error) {
return ParseSignatureInPackage(nil, expr)
}
// ParseSignatureInPackage builds a Signature by parsing a Go function type
// expression. The expression may reference types in the provided package.
func ParseSignatureInPackage(pkg *types.Package, expr string) (*Signature, error) {
tv, err := types.Eval(token.NewFileSet(), pkg, token.NoPos, expr)
if err != nil {
return nil, err
}
if tv.Value != nil {
return nil, errors.New("signature expression should have nil value")
}
s, ok := tv.Type.(*types.Signature)
if !ok {
return nil, errors.New("provided type is not a function signature")
}
return NewSignature(pkg, s), nil
}
// Params returns the function signature argument types.
func (s *Signature) Params() *Tuple { return s.params }
// Results returns the function return types.
func (s *Signature) Results() *Tuple { return s.results }
// Bytes returns the total size of the function arguments and return values.
func (s *Signature) Bytes() int { return s.Params().Bytes() + s.Results().Bytes() }
// String writes Signature as a string. This does not include the "func" keyword.
func (s *Signature) String() string {
var buf bytes.Buffer
types.WriteSignature(&buf, s.sig, func(pkg *types.Package) string {
if pkg == s.pkg {
return ""
}
return pkg.Name()
})
return buf.String()
}
func (s *Signature) init() {
p := s.sig.Params()
r := s.sig.Results()
// Compute parameter offsets.
vs := tuplevars(p)
vs = append(vs, types.NewParam(token.NoPos, nil, "sentinel", types.Typ[types.Uint64]))
paramsoffsets := Sizes.Offsetsof(vs)
paramssize := paramsoffsets[p.Len()]
s.params = newTuple(p, paramsoffsets, paramssize, "arg")
// Result offsets.
vs = tuplevars(r)
resultsoffsets := Sizes.Offsetsof(vs)
var resultssize int64
if n := len(vs); n > 0 {
resultssize = resultsoffsets[n-1] + Sizes.Sizeof(vs[n-1].Type())
}
for i := range resultsoffsets {
resultsoffsets[i] += paramssize
}
s.results = newTuple(r, resultsoffsets, resultssize, "ret")
}
// Tuple represents a tuple of variables, such as function arguments or results.
type Tuple struct {
components []Component
byname map[string]Component
size int
}
func newTuple(t *types.Tuple, offsets []int64, size int64, defaultprefix string) *Tuple {
tuple := &Tuple{
byname: map[string]Component{},
size: int(size),
}
for i := 0; i < t.Len(); i++ {
v := t.At(i)
name := v.Name()
if name == "" {
name = defaultprefix
if i > 0 {
name += strconv.Itoa(i)
}
}
addr := operand.NewParamAddr(name, int(offsets[i]))
c := NewComponent(v.Type(), addr)
tuple.components = append(tuple.components, c)
if v.Name() != "" {
tuple.byname[v.Name()] = c
}
}
return tuple
}
// Lookup returns the variable with the given name.
func (t *Tuple) Lookup(name string) Component {
e := t.byname[name]
if e == nil {
return errorf("unknown variable \"%s\"", name)
}
return e
}
// At returns the variable at index i.
func (t *Tuple) At(i int) Component {
if i >= len(t.components) {
return errorf("index out of range")
}
return t.components[i]
}
// Bytes returns the size of the Tuple. This may include additional padding.
func (t *Tuple) Bytes() int { return t.size }
func tuplevars(t *types.Tuple) []*types.Var {
vs := make([]*types.Var, t.Len())
for i := 0; i < t.Len(); i++ {
vs[i] = t.At(i)
}
return vs
}
|