summaryrefslogtreecommitdiff
path: root/vendor/github.com/mmcloughlin/avo/gotypes/signature.go
blob: e00002034d8691b4b7a3bbe36434d6b90e7dcf94 (plain)
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
}