1// Copyright 2017 The Wuffs Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package cgen
16
17import (
18	"errors"
19	"fmt"
20
21	a "github.com/google/wuffs/lang/ast"
22	t "github.com/google/wuffs/lang/token"
23)
24
25var errNeedDerivedVar = errors.New("cgen: internal error: need derived var")
26
27func (g *gen) needDerivedVar(name t.ID) bool {
28	for _, o := range g.currFunk.astFunc.Body() {
29		err := o.Walk(func(p *a.Node) error {
30			// Look for p matching "args.name.etc(etc)".
31			if p.Kind() != a.KExpr {
32				return nil
33			}
34			q := p.AsExpr()
35			if q.Operator() != t.IDOpenParen {
36				return nil
37			}
38			q = q.LHS().AsExpr()
39			if q.Operator() != t.IDDot {
40				return nil
41			}
42			q = q.LHS().AsExpr()
43			if q.Operator() != t.IDDot || q.Ident() != name {
44				return nil
45			}
46			q = q.LHS().AsExpr()
47			if q.Operator() != 0 || q.Ident() != t.IDArgs {
48				return nil
49			}
50			return errNeedDerivedVar
51		})
52		if err == errNeedDerivedVar {
53			return true
54		}
55	}
56	return false
57}
58
59func (g *gen) findDerivedVars() {
60	for _, o := range g.currFunk.astFunc.In().Fields() {
61		o := o.AsField()
62		if !o.XType().IsIOType() || !g.needDerivedVar(o.Name()) {
63			continue
64		}
65		if g.currFunk.derivedVars == nil {
66			g.currFunk.derivedVars = map[t.ID]struct{}{}
67		}
68		g.currFunk.derivedVars[o.Name()] = struct{}{}
69	}
70}
71
72func (g *gen) writeLoadDerivedVar(b *buffer, hack string, prefix string, name t.ID, typ *a.TypeExpr, header bool) error {
73	// TODO: remove this hack. We're picking up the wrong name for "src:r,
74	// dummy:args.src".
75	if name.Str(g.tm) == "dummy" {
76		name = g.tm.ByName("src")
77	}
78	// TODO: also remove this hack.
79	if hack == "w" {
80		b.printf("%s%sw = %sw.data.ptr + %sw.meta.wi;\n", iopPrefix, vPrefix, uPrefix, uPrefix)
81		return nil
82	} else if hack == "r" {
83		b.printf("%s%sr = %sr.data.ptr + %sr.meta.ri;\n", iopPrefix, vPrefix, uPrefix, uPrefix)
84		return nil
85	}
86
87	if !typ.IsIOType() {
88		return nil
89	}
90	if g.currFunk.derivedVars == nil {
91		return nil
92	}
93	if _, ok := g.currFunk.derivedVars[name]; !ok {
94		return nil
95	}
96
97	preName := prefix + name.Str(g.tm)
98	i1, i2 := "meta.ri", "meta.wi"
99	if typ.QID()[1] == t.IDIOWriter {
100		i1, i2 = "meta.wi", "data.len"
101	}
102
103	if header {
104		b.printf("uint8_t* %s%s = NULL;", iopPrefix, preName)
105		b.printf("uint8_t* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;", io0Prefix, preName)
106		b.printf("uint8_t* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;", io1Prefix, preName)
107		b.printf("uint8_t* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;", io2Prefix, preName)
108	}
109
110	b.printf("if (%s) {", preName)
111
112	if header {
113		b.printf("%s%s = %s->data.ptr;", io0Prefix, preName, preName)
114		b.printf("%s%s = %s%s + %s->%s;", io1Prefix, preName, io0Prefix, preName, preName, i1)
115		b.printf("%s%s = %s%s;", iopPrefix, preName, io1Prefix, preName)
116		b.printf("%s%s = %s%s + %s->%s;", io2Prefix, preName, io0Prefix, preName, preName, i2)
117
118		if typ.QID()[1] == t.IDIOWriter {
119			b.printf("if (%s->meta.closed) {", preName)
120			b.printf("%s%s = %s%s;", io2Prefix, preName, iopPrefix, preName)
121			b.printf("}\n")
122		}
123	} else {
124		b.printf("%s%s = %s->data.ptr + %s->%s;", iopPrefix, preName, preName, preName, i1)
125	}
126
127	b.printf("}\n")
128	return nil
129}
130
131func (g *gen) writeSaveDerivedVar(b *buffer, hack string, prefix string, name t.ID, typ *a.TypeExpr) error {
132	// TODO: remove this hack. We're picking up the wrong name for "src:r,
133	// dummy:args.src".
134	if name.Str(g.tm) == "dummy" {
135		name = g.tm.ByName("src")
136	}
137	// TODO: also remove this hack.
138	if hack == "w" {
139		b.printf("%sw.meta.wi = ((size_t)(%s%sw - %sw.data.ptr));\n", uPrefix, iopPrefix, vPrefix, uPrefix)
140		return nil
141	} else if hack == "r" {
142		b.printf("%sr.meta.ri = ((size_t)(%s%sr - %sr.data.ptr));\n", uPrefix, iopPrefix, vPrefix, uPrefix)
143		return nil
144	}
145
146	if !typ.IsIOType() {
147		return nil
148	}
149	if g.currFunk.derivedVars == nil {
150		return nil
151	}
152	if _, ok := g.currFunk.derivedVars[name]; !ok {
153		return nil
154	}
155
156	preName := prefix + name.Str(g.tm)
157	index := "ri"
158	if typ.QID()[1] == t.IDIOWriter {
159		index = "wi"
160	}
161
162	b.printf("if (%s) { %s->meta.%s = ((size_t)(%s%s - %s->data.ptr)); }\n",
163		preName, preName, index, iopPrefix, preName, preName)
164	return nil
165}
166
167func (g *gen) writeLoadExprDerivedVars(b *buffer, n *a.Expr) error {
168	if g.currFunk.derivedVars == nil {
169		return nil
170	}
171	if n.Operator() != t.IDOpenParen {
172		return nil
173	}
174	for _, o := range n.Args() {
175		o := o.AsArg()
176		prefix := aPrefix
177		// TODO: don't hard-code these.
178		hack := ""
179		if s := o.Value().Str(g.tm); s != "args.dst" && s != "args.src" && s != "w" && s != "r" {
180			continue
181		} else if s == "w" || s == "r" {
182			prefix = vPrefix
183			hack = s
184		}
185		if err := g.writeLoadDerivedVar(b, hack, prefix, o.Name(), o.Value().MType(), false); err != nil {
186			return err
187		}
188	}
189	return nil
190}
191
192func (g *gen) writeSaveExprDerivedVars(b *buffer, n *a.Expr) error {
193	if g.currFunk.derivedVars == nil {
194		return nil
195	}
196	if n.Operator() != t.IDOpenParen {
197		return nil
198	}
199	for _, o := range n.Args() {
200		o := o.AsArg()
201		prefix := aPrefix
202		// TODO: don't hard-code these.
203		hack := ""
204		if s := o.Value().Str(g.tm); s != "args.dst" && s != "args.src" && s != "w" && s != "r" {
205			continue
206		} else if s == "w" || s == "r" {
207			prefix = vPrefix
208			hack = s
209		}
210		if err := g.writeSaveDerivedVar(b, hack, prefix, o.Name(), o.Value().MType()); err != nil {
211			return err
212		}
213	}
214	return nil
215}
216
217func (g *gen) writeResumeSuspend1(b *buffer, f *funk, n *a.Var, suspend bool) error {
218	if typ := n.XType(); typ.HasPointers() {
219		return nil
220	} else if f.varResumables == nil || !f.varResumables[n.Name()] {
221		return nil
222	} else {
223		local := fmt.Sprintf("%s%s", vPrefix, n.Name().Str(g.tm))
224		lhs := local
225		// TODO: don't hard-code [0], and allow recursive coroutines.
226		rhs := fmt.Sprintf("self->private_data.%s%s[0].%s", sPrefix, g.currFunk.astFunc.FuncName().Str(g.tm), lhs)
227		if suspend {
228			lhs, rhs = rhs, lhs
229		}
230
231		switch typ.Decorator() {
232		case 0:
233			b.printf("%s = %s;\n", lhs, rhs)
234			return nil
235		case t.IDArray:
236			inner := typ.Inner()
237			if inner.Decorator() != 0 {
238				break
239			}
240			qid := inner.QID()
241			if qid[0] != t.IDBase {
242				break
243			}
244			switch qid[1] {
245			case t.IDU8, t.IDU16, t.IDU32, t.IDU64:
246				b.printf("memcpy(%s, %s, sizeof(%s));\n", lhs, rhs, local)
247				return nil
248			}
249		}
250	}
251
252	return fmt.Errorf("cannot resume or suspend a local variable %q of type %q",
253		n.Name().Str(g.tm), n.XType().Str(g.tm))
254}
255
256func (g *gen) writeResumeSuspend(b *buffer, f *funk, suspend bool) error {
257	for _, n := range f.varList {
258		if err := g.writeResumeSuspend1(b, f, n, suspend); err != nil {
259			return err
260		}
261	}
262	return nil
263}
264
265func (g *gen) writeVars(b *buffer, f *funk, inStructDecl bool) error {
266	for _, n := range f.varList {
267		typ := n.XType()
268		if inStructDecl {
269			if typ.HasPointers() || f.varResumables == nil || !f.varResumables[n.Name()] {
270				continue
271			}
272		}
273
274		name := n.Name().Str(g.tm)
275
276		if typ.IsIOType() {
277			b.printf("wuffs_base__io_buffer %s%s = wuffs_base__empty_io_buffer();\n", uPrefix, name)
278		}
279
280		if err := g.writeCTypeName(b, typ, vPrefix, name); err != nil {
281			return err
282		}
283		if inStructDecl {
284			b.writes(";\n")
285		} else if typ.IsNumType() {
286			b.writes(" = 0;\n")
287		} else if typ.IsBool() {
288			b.writes(" = false;\n")
289		} else if typ.IsStatus() {
290			b.writes(" = NULL;\n")
291		} else if typ.IsIOType() {
292			b.printf(" = &%s%s;\n", uPrefix, name)
293		} else {
294			b.writes(" = {0};\n")
295		}
296
297		if typ.IsIOType() {
298			b.printf("uint8_t* %s%s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", iopPrefix, vPrefix, name)
299			b.printf("uint8_t* %s%s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", io0Prefix, vPrefix, name)
300			b.printf("uint8_t* %s%s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", io1Prefix, vPrefix, name)
301			b.printf("uint8_t* %s%s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", io2Prefix, vPrefix, name)
302		}
303	}
304	return nil
305}
306