File: context.go

package info (click to toggle)
golang-github-mmcloughlin-avo 0.5.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 15,024 kB
  • sloc: xml: 71,029; asm: 14,862; sh: 194; makefile: 21; ansic: 11
file content (224 lines) | stat: -rw-r--r-- 5,955 bytes parent folder | download
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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
package build

import (
	"errors"
	"fmt"
	"go/types"

	"golang.org/x/tools/go/packages"

	"github.com/mmcloughlin/avo/attr"
	"github.com/mmcloughlin/avo/buildtags"
	"github.com/mmcloughlin/avo/gotypes"
	"github.com/mmcloughlin/avo/ir"
	"github.com/mmcloughlin/avo/operand"
	"github.com/mmcloughlin/avo/reg"
)

//go:generate avogen -output zinstructions.go build
//go:generate avogen -output zinstructions_test.go buildtest

// Context maintains state for incrementally building an avo File.
type Context struct {
	pkg      *packages.Package
	file     *ir.File
	function *ir.Function
	global   *ir.Global
	errs     ErrorList
	reg.Collection
}

// NewContext initializes an empty build Context.
func NewContext() *Context {
	return &Context{
		file:       ir.NewFile(),
		Collection: *reg.NewCollection(),
	}
}

// Package sets the package the generated file will belong to. Required to be able to reference types in the package.
func (c *Context) Package(path string) {
	cfg := &packages.Config{
		Mode: packages.NeedTypes | packages.NeedDeps | packages.NeedImports,
	}
	pkgs, err := packages.Load(cfg, path)
	if err != nil {
		c.adderror(err)
		return
	}
	pkg := pkgs[0]
	if len(pkg.Errors) > 0 {
		for _, err := range pkg.Errors {
			c.adderror(err)
		}
		return
	}
	c.pkg = pkg
}

// Constraints sets build constraints for the file.
func (c *Context) Constraints(t buildtags.ConstraintsConvertable) {
	cs := t.ToConstraints()
	if err := cs.Validate(); err != nil {
		c.adderror(err)
		return
	}
	c.file.Constraints = cs
}

// Constraint appends a constraint to the file's build constraints.
func (c *Context) Constraint(t buildtags.ConstraintConvertable) {
	c.Constraints(append(c.file.Constraints, t.ToConstraint()))
}

// ConstraintExpr appends a constraint to the file's build constraints. The
// constraint to add is parsed from the given expression. The expression should
// look the same as the content following "// +build " in regular build
// constraint comments.
func (c *Context) ConstraintExpr(expr string) {
	constraint, err := buildtags.ParseConstraint(expr)
	if err != nil {
		c.adderror(err)
		return
	}
	c.Constraint(constraint)
}

// Function starts building a new function with the given name.
func (c *Context) Function(name string) {
	c.function = ir.NewFunction(name)
	c.file.AddSection(c.function)
}

// Doc sets documentation comment lines for the currently active function.
func (c *Context) Doc(lines ...string) {
	c.activefunc().Doc = lines
}

// Pragma adds a compiler directive to the currently active function.
func (c *Context) Pragma(directive string, args ...string) {
	c.activefunc().AddPragma(directive, args...)
}

// Attributes sets function attributes for the currently active function.
func (c *Context) Attributes(a attr.Attribute) {
	c.activefunc().Attributes = a
}

// Signature sets the signature for the currently active function.
func (c *Context) Signature(s *gotypes.Signature) {
	c.activefunc().SetSignature(s)
}

// SignatureExpr parses the signature expression and sets it as the active function's signature.
func (c *Context) SignatureExpr(expr string) {
	s, err := gotypes.ParseSignatureInPackage(c.types(), expr)
	if err != nil {
		c.adderror(err)
		return
	}
	c.Signature(s)
}

// Implement starts building a function of the given name, whose type is
// specified by a stub in the containing package.
func (c *Context) Implement(name string) {
	pkg := c.types()
	if pkg == nil {
		c.adderrormessage("no package specified")
		return
	}
	s, err := gotypes.LookupSignature(pkg, name)
	if err != nil {
		c.adderror(err)
		return
	}
	c.Function(name)
	c.Signature(s)
}

func (c *Context) types() *types.Package {
	if c.pkg == nil {
		return nil
	}
	return c.pkg.Types
}

// AllocLocal allocates size bytes in the stack of the currently active function.
// Returns a reference to the base pointer for the newly allocated region.
func (c *Context) AllocLocal(size int) operand.Mem {
	return c.activefunc().AllocLocal(size)
}

// Instruction adds an instruction to the active function.
func (c *Context) Instruction(i *ir.Instruction) {
	c.activefunc().AddInstruction(i)
}

// Label adds a label to the active function.
func (c *Context) Label(name string) {
	c.activefunc().AddLabel(ir.Label(name))
}

// Comment adds comment lines to the active function.
func (c *Context) Comment(lines ...string) {
	c.activefunc().AddComment(lines...)
}

// Commentf adds a formtted comment line.
func (c *Context) Commentf(format string, a ...interface{}) {
	c.Comment(fmt.Sprintf(format, a...))
}

func (c *Context) activefunc() *ir.Function {
	if c.function == nil {
		c.adderrormessage("no active function")
		return ir.NewFunction("")
	}
	return c.function
}

// StaticGlobal adds a new static data section to the file and returns a pointer to it.
func (c *Context) StaticGlobal(name string) operand.Mem {
	c.global = ir.NewStaticGlobal(name)
	c.file.AddSection(c.global)
	return c.global.Base()
}

// DataAttributes sets the attributes on the current active global data section.
func (c *Context) DataAttributes(a attr.Attribute) {
	c.activeglobal().Attributes = a
}

// AddDatum adds constant v at offset to the current active global data section.
func (c *Context) AddDatum(offset int, v operand.Constant) {
	if err := c.activeglobal().AddDatum(ir.NewDatum(offset, v)); err != nil {
		c.adderror(err)
	}
}

// AppendDatum appends a constant to the current active global data section.
func (c *Context) AppendDatum(v operand.Constant) {
	c.activeglobal().Append(v)
}

func (c *Context) activeglobal() *ir.Global {
	if c.global == nil {
		c.adderrormessage("no active global")
		return ir.NewStaticGlobal("")
	}
	return c.global
}

func (c *Context) adderror(err error) {
	c.errs.addext(err)
}

func (c *Context) adderrormessage(msg string) {
	c.adderror(errors.New(msg))
}

// Result returns the built file and any accumulated errors.
func (c *Context) Result() (*ir.File, error) {
	return c.file, c.errs.Err()
}