1package main
2
3import (
4 "fmt"
5 "path/filepath"
6 "strconv"
7 "strings"
8)
9
10type Request struct {
11 Name string
12 Opcode uint8
13 DataByte *Field
14 Fixed int
15 Params, Fields []Field
16 Node *Node
17 Derived map[string]string
18 Lists []Field
19 Values *ValueList
20}
21
22// resolveRequests lowers scalar, list and mask-bitcase value-list requests.
23func resolveRequests(m *Module) ([]Request, error) {
24 var out []Request
25 for _, n := range m.Root.Children {
26 if n.Kind != "request" {
27 continue
28 }
29 name := goName(n.Attr["name"])
30 if err := m.claim(name, n); err != nil {
31 return nil, err
32 }
33 opcode, _ := strconv.Atoi(n.Attr["opcode"])
34 r := Request{Name: name, Opcode: uint8(opcode), Node: n, Derived: map[string]string{}}
35 var err error
36 r.Values, err = resolveValues(m, n, name)
37 if err != nil {
38 return nil, err
39 }
40 refs := map[string]int{}
41 var countRefs func(*Node)
42 countRefs = func(x *Node) {
43 if x.Kind == "reply" || x.Kind == "doc" {
44 return
45 }
46 if x.Kind == "fieldref" {
47 refs[x.Text]++
48 }
49 for _, c := range x.Children {
50 countRefs(c)
51 }
52 }
53 countRefs(n)
54 for _, x := range n.Children {
55 if x.Kind == "list" && len(x.Children) == 1 && x.Children[0].Kind == "fieldref" && refs[x.Children[0].Text] == 1 {
56 r.Derived[x.Children[0].Text] = paramName(x.Attr["name"])
57 }
58 }
59 dynamic := false
60 off := 4
61 first := true
62 for _, n := range n.Children {
63 if n.Kind == "reply" || n.Kind == "doc" {
64 continue
65 }
66 if n.Kind == "switch" {
67 r.Params = append(r.Params, Field{Param: "v", GoType: "*" + name + "Values"})
68 continue
69 }
70 f := Field{Name: goName(n.Attr["name"]), Param: paramName(n.Attr["name"]), Kind: n.Kind, Node: n, Off: off - 4}
71 if n.Kind == "pad" {
72 if n.Attr["align"] != "" {
73 a, _ := strconv.Atoi(n.Attr["align"])
74 f.Size = (-off) & (a - 1)
75 } else {
76 f.Size, _ = strconv.Atoi(n.Attr["bytes"])
77 }
78 } else {
79 typ, err := lookup(m, n.Attr["type"])
80 if err != nil {
81 return nil, n.fail("%v", err)
82 }
83 if typ.Variable && n.Kind != "list" {
84 return nil, n.fail("variable field requires list support")
85 }
86 f.Type = typ
87 f.Size = typ.Size
88 f.GoType = qualified(m, typ.Module, typ.GoType)
89 if enum := n.Attr["enum"]; enum != "" {
90 e, owner := findEnum(m, enum)
91 if e == nil {
92 return nil, n.fail("unknown enum %s", enum)
93 }
94 f.GoType = qualified(m, owner, e.Name)
95 }
96 if mask := n.Attr["mask"]; mask != "" {
97 e, owner := findEnum(m, mask)
98 if e == nil {
99 return nil, n.fail("unknown mask %s", mask)
100 }
101 f.GoType = qualified(m, owner, e.Name)
102 }
103 if n.Kind == "list" {
104 f.Size = 0
105 f.GoType = "[]" + f.GoType
106 if n.Attr["type"] == "char" {
107 f.GoType = "string"
108 }
109 if n.Attr["type"] == "void" || n.Attr["type"] == "BYTE" || n.Attr["type"] == "CARD8" {
110 f.GoType = "[]byte"
111 }
112 if len(n.Children) > 0 {
113 f.Expr = n.Children[0]
114 }
115 if m.Header == "xproto" && r.Name == "SendEvent" {
116 f.GoType = "*[32]byte"
117 }
118 r.Lists = append(r.Lists, f)
119 r.Params = append(r.Params, f)
120 } else if n.Kind == "field" && r.Derived[n.Attr["name"]] == "" {
121 if r.Values != nil && n == r.Values.Mask.Node {
122 f.Param = "v.Mask"
123 } else {
124 r.Params = append(r.Params, f)
125 }
126 }
127 if n.Kind == "exprfield" {
128 f.Expr = n.Children[0]
129 }
130 }
131 // Core's first XML byte is header byte 1; extensions have no data byte.
132 if first && m.ExtName == "" {
133 if f.Size != 1 {
134 return nil, n.fail("core request first member must occupy header data byte")
135 }
136 if f.Kind != "pad" {
137 copy := f
138 r.DataByte = ©
139 }
140 first = false
141 continue
142 }
143 first = false
144 if f.Kind == "list" {
145 dynamic = true
146 }
147 if dynamic {
148 f.Off = -1
149 }
150 if f.Kind == "pad" && n.Attr["align"] != "" {
151 f.Align, _ = strconv.Atoi(n.Attr["align"])
152 }
153 r.Fields = append(r.Fields, f)
154 off += f.Size
155 }
156 r.Fixed = off - 4
157 out = append(out, r)
158 }
159 return out, nil
160}
161func requestDecl(m *Module, r Request) (string, error) {
162 var b strings.Builder
163 if r.Values != nil {
164 b.WriteString(valuesDecl(r.Name, r.Values))
165 }
166 fmt.Fprintf(&b, "func %s(b *xturbo.Buf", r.Name)
167 for _, p := range r.Params {
168 fmt.Fprintf(&b, ", %s %s", p.Param, p.GoType)
169 }
170 b.WriteString(") xturbo.Seq {\n")
171 ref := func(s string) string {
172 // xcb-proto permits an exprfield to refer to a trailing list's
173 // implicit <list>_len (QueryTextExtents.string_len).
174 declared := false
175 for _, n := range r.Node.Children {
176 if n.Kind == "field" && n.Attr["name"] == s {
177 declared = true
178 }
179 }
180 for _, list := range r.Lists {
181 if !declared && s == list.Node.Attr["name"]+"_len" {
182 return "len(" + list.Param + ")"
183 }
184 }
185 if list := r.Derived[s]; list != "" {
186 return "len(" + list + ")"
187 }
188 return "int(" + paramName(s) + ")"
189 }
190 // Derived lengths are cast only after checking their wire width.
191 for _, f := range append(append([]Field{}, r.Fields...), r.Params...) {
192 if f.Kind != "field" || r.Derived[f.Node.Attr["name"]] == "" {
193 continue
194 }
195 if f.Type.Size < 8 {
196 fmt.Fprintf(&b, "if uint64(%s)>%d { b.Fail(xturbo.ErrBadLength); return 0 };", ref(f.Node.Attr["name"]), uint64(1)<<(8*f.Type.Size)-1)
197 }
198 }
199 if r.DataByte != nil {
200 f := r.DataByte
201 if list := r.Derived[f.Node.Attr["name"]]; list != "" {
202 fmt.Fprintf(&b, "if len(%s)>255 { b.Fail(xturbo.ErrBadLength); return 0 };", list)
203 }
204 }
205 for _, f := range r.Lists {
206 if f.Expr != nil && !(f.Expr.Kind == "fieldref" && r.Derived[f.Expr.Text] != "") {
207 ex, err := renderExpr(f.Expr, ref, m)
208 if err != nil {
209 return "", err
210 }
211 fmt.Fprintf(&b, "if len(%s)!=%s { b.Fail(xturbo.ErrBadLength); return 0 };", f.Param, ex)
212 }
213 }
214 body := fmt.Sprint((r.Fixed + 3) &^ 3)
215 if len(r.Lists) > 0 {
216 b.WriteString("size:=0;")
217 for _, f := range r.Fields {
218 if f.Kind == "list" {
219 if f.Type.Variable {
220 fmt.Fprintf(&b, "for i:=range %s { size+=%s[i].Size() };", f.Param, f.Param)
221 } else {
222 fmt.Fprintf(&b, "size+=len(%s)*%d;", f.Param, f.Type.Size)
223 }
224 } else if f.Align > 0 {
225 fmt.Fprintf(&b, "size=(size+%d)&^%d;", f.Align-1, f.Align-1)
226 } else {
227 fmt.Fprintf(&b, "size+=%d;", f.Size)
228 }
229 }
230 b.WriteString("size=(size+3)&^3;")
231 body = "size"
232 }
233 if r.Values != nil {
234 body += "+4*bits.OnesCount32(uint32(v.Mask))"
235 }
236 major := fmt.Sprint(r.Opcode)
237 data := "0"
238 if m.ExtName != "" {
239 fmt.Fprintf(&b, "if b.Ext==nil { b.Fail(xturbo.ErrExtMissing); return 0 }; major:=b.Ext.Major(xturbo.Ext%s); if major==0 { b.Fail(xturbo.ErrExtMissing); return 0 };\n", m.ExtName)
240 major = "major"
241 data = fmt.Sprint(r.Opcode)
242 } else if r.DataByte != nil {
243 f := r.DataByte
244 if f.Type.Base == "bool" {
245 value := f.Param
246 if f.Kind == "exprfield" {
247 ex, err := renderExpr(f.Expr, ref, m)
248 if err != nil {
249 return "", err
250 }
251 value = "(" + ex + " != 0)"
252 }
253 fmt.Fprintf(&b, "var data uint8; if %s { data=1 };", value)
254 data = "data"
255 } else {
256 data = "uint8(" + ref(f.Node.Attr["name"]) + ")"
257 }
258 }
259 fmt.Fprintf(&b, "p,seq:=b.Request(%s,%s,%s); if p==nil { return 0 };\n", major, data, body)
260 if len(r.Lists) > 0 {
261 b.WriteString("off:=0;")
262 }
263 for _, f := range r.Fields {
264 if f.Kind == "pad" {
265 if len(r.Lists) > 0 {
266 if f.Align > 0 {
267 fmt.Fprintf(&b, "off=(off+%d)&^%d;", f.Align-1, f.Align-1)
268 } else {
269 fmt.Fprintf(&b, "off+=%d;", f.Size)
270 }
271 }
272 continue
273 }
274 if f.Kind == "list" {
275 if f.GoType == "*[32]byte" {
276 fmt.Fprintf(&b, "copy(p[off:],%s[:]);", f.Param)
277 } else if f.GoType == "string" || f.GoType == "[]byte" {
278 fmt.Fprintf(&b, "copy(p[off:],%s); off+=len(%s);", f.Param, f.Param)
279 } else {
280 fmt.Fprintf(&b, "for i:=range %s {", f.Param)
281 if isScalar(f.Type) {
282 b.WriteString(putScalar(f.Type, f.Param+"[i]", "p[off:]"))
283 } else {
284 fmt.Fprintf(&b, "%s[i].Put(p[off:]);", f.Param)
285 }
286 if f.Type.Variable {
287 fmt.Fprintf(&b, "off+=%s[i].Size();", f.Param)
288 } else {
289 fmt.Fprintf(&b, "off+=%d;", f.Type.Size)
290 }
291 b.WriteString("};")
292 }
293 continue
294 }
295 src := f.Param
296 if list := r.Derived[f.Node.Attr["name"]]; list != "" {
297 src = "len(" + list + ")"
298 }
299 if f.Kind == "exprfield" {
300 var err error
301 src, err = renderExpr(f.Expr, ref, m)
302 if err != nil {
303 return "", err
304 }
305 }
306 dst := fmt.Sprintf("p[%d:]", f.Off)
307 if len(r.Lists) > 0 {
308 dst = "p[off:]"
309 }
310 if isScalar(f.Type) {
311 b.WriteString(putScalar(f.Type, src, dst))
312 } else {
313 fmt.Fprintf(&b, "%s.Put(%s);", src, dst)
314 }
315 if len(r.Lists) > 0 {
316 fmt.Fprintf(&b, "off+=%d;", f.Size)
317 }
318 }
319 if r.Values != nil {
320 fmt.Fprintf(&b, "off:=%d;", r.Fixed)
321 for _, f := range r.Values.Fields {
322 fmt.Fprintf(&b, "if v.Mask & %s != 0 {", f.Bit)
323 b.WriteString(putScalar(f.Field.Type, "v."+f.Field.Name, "p[off:]"))
324 b.WriteString("off+=4 };")
325 }
326 b.WriteString("_ = off;")
327 }
328 b.WriteString("return seq }\n")
329 return b.String(), nil
330}
331func emitRequests(modules []*Module) error {
332 for _, m := range modules {
333 requests, err := resolveRequests(m)
334 if err != nil {
335 return err
336 }
337 data := struct {
338 Pkg string
339 Decls []string
340 }{Pkg: m.Pkg}
341 for _, r := range requests {
342 decl, err := requestDecl(m, r)
343 if err != nil {
344 return err
345 }
346 data.Decls = append(data.Decls, decl)
347 }
348 all := strings.Join(data.Decls, "\n")
349 imp := importsFor(m, all)
350 if len(requests) > 0 {
351 imp += "\nimport \"bnema.dev/xturbo\""
352 }
353 data.Decls = append([]string{imp}, data.Decls...)
354 if err := writeTemplate(filepath.Join(m.Pkg, "requests_generated.go"), "requests", data); err != nil {
355 return err
356 }
357 }
358 return nil
359}