| // Copyright 2017 The Wuffs Authors. | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //    https://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | package cgen | 
 |  | 
 | import ( | 
 | 	"errors" | 
 | 	"fmt" | 
 |  | 
 | 	a "github.com/google/wuffs/lang/ast" | 
 | 	t "github.com/google/wuffs/lang/token" | 
 | ) | 
 |  | 
 | var errNeedDerivedVar = errors.New("cgen: internal error: need derived var") | 
 |  | 
 | // argsContainsArgsDotFoo returns whether the argNodes, a slice of *ast.Node | 
 | // where each ast.Node is an ast.Arg, contains a "name: value" element whose | 
 | // value is "args.foo". | 
 | func argsContainsArgsDotFoo(argNodes []*a.Node, foo t.ID) bool { | 
 | 	for _, o := range argNodes { | 
 | 		if foo == o.AsArg().Value().IsArgsDotFoo() { | 
 | 			return true | 
 | 		} | 
 | 	} | 
 | 	return false | 
 | } | 
 |  | 
 | func (g *gen) needDerivedVar(name t.ID) bool { | 
 | 	if name == 0 { | 
 | 		return false | 
 | 	} | 
 | 	for _, o := range g.currFunk.astFunc.Body() { | 
 | 		err := o.Walk(func(p *a.Node) error { | 
 | 			switch p.Kind() { | 
 | 			case a.KExpr: | 
 | 				// Look for p matching "args.name.etc(etc)". | 
 | 				recv, meth, args, ok := p.AsExpr().IsMethodCall() | 
 | 				if !ok { | 
 | 					return nil | 
 | 				} | 
 | 				if recv.IsArgsDotFoo() == name { | 
 | 					return errNeedDerivedVar | 
 | 				} | 
 | 				// Some built-in methods will also need a derived var for their | 
 | 				// arguments. | 
 | 				// | 
 | 				// TODO: use a comprehensive list of such methods. | 
 | 				switch meth { | 
 | 				case t.IDLimitedSwizzleU32InterleavedFromReader, | 
 | 					t.IDSwizzleInterleavedFromReader: | 
 | 					if recv.MType().Eq(typeExprPixelSwizzler) && argsContainsArgsDotFoo(args, name) { | 
 | 						return errNeedDerivedVar | 
 | 					} | 
 | 				} | 
 |  | 
 | 			case a.KIOManip: | 
 | 				if p.AsIOManip().IO().IsArgsDotFoo() == name { | 
 | 					return errNeedDerivedVar | 
 | 				} | 
 | 			} | 
 | 			return nil | 
 | 		}) | 
 | 		if err == errNeedDerivedVar { | 
 | 			return true | 
 | 		} | 
 | 	} | 
 | 	return false | 
 | } | 
 |  | 
 | func (g *gen) findDerivedVars() { | 
 | 	for _, o := range g.currFunk.astFunc.In().Fields() { | 
 | 		o := o.AsField() | 
 | 		if !o.XType().IsIOTokenType() || !g.needDerivedVar(o.Name()) { | 
 | 			continue | 
 | 		} | 
 | 		if g.currFunk.derivedVars == nil { | 
 | 			g.currFunk.derivedVars = map[t.ID]struct{}{} | 
 | 		} | 
 | 		g.currFunk.derivedVars[o.Name()] = struct{}{} | 
 | 	} | 
 | } | 
 |  | 
 | func (g *gen) derivedVarCNames(typ *a.TypeExpr) (elem string, i1 string, i2 string, isWriter bool, retErr error) { | 
 | 	if typ.Decorator() == 0 { | 
 | 		if qid := typ.QID(); qid[0] == t.IDBase { | 
 | 			switch qid[1] { | 
 | 			case t.IDIOReader: | 
 | 				return "uint8_t", "meta.ri", "meta.wi", false, nil | 
 | 			case t.IDIOWriter: | 
 | 				return "uint8_t", "meta.wi", "data.len", true, nil | 
 | 			case t.IDTokenReader: | 
 | 				return "wuffs_base__token", "meta.ri", "meta.wi", false, nil | 
 | 			case t.IDTokenWriter: | 
 | 				return "wuffs_base__token", "meta.wi", "data.len", true, nil | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	return "", "", "", false, fmt.Errorf("unsupported derivedVarCNames type %q", typ.Str(g.tm)) | 
 | } | 
 |  | 
 | func (g *gen) writeInitialLoadDerivedVar(b *buffer, n *a.Field) error { | 
 | 	elem, i1, i2, isWriter, err := g.derivedVarCNames(n.XType()) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 | 	preName := aPrefix + n.Name().Str(g.tm) | 
 | 	c := "" | 
 | 	if !isWriter { | 
 | 		c = "const " | 
 | 	} | 
 |  | 
 | 	b.printf("%s%s* %s%s = NULL;\n", c, elem, iopPrefix, preName) | 
 | 	b.printf("%s%s* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", c, elem, io0Prefix, preName) | 
 | 	b.printf("%s%s* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", c, elem, io1Prefix, preName) | 
 | 	b.printf("%s%s* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", c, elem, io2Prefix, preName) | 
 |  | 
 | 	b.printf("if (%s) {\n", preName) | 
 |  | 
 | 	b.printf("%s%s = %s->data.ptr;\n", io0Prefix, preName, preName) | 
 | 	b.printf("%s%s = %s%s + %s->%s;\n", io1Prefix, preName, io0Prefix, preName, preName, i1) | 
 | 	b.printf("%s%s = %s%s;\n", iopPrefix, preName, io1Prefix, preName) | 
 | 	b.printf("%s%s = %s%s + %s->%s;\n", io2Prefix, preName, io0Prefix, preName, preName, i2) | 
 |  | 
 | 	if isWriter { | 
 | 		b.printf("if (%s->meta.closed) {\n", preName) | 
 | 		b.printf("%s%s = %s%s;\n", io2Prefix, preName, iopPrefix, preName) | 
 | 		b.printf("}\n") | 
 | 	} | 
 |  | 
 | 	b.printf("}\n") | 
 | 	return nil | 
 | } | 
 |  | 
 | func (g *gen) writeFinalSaveDerivedVar(b *buffer, n *a.Field) error { | 
 | 	_, i1, _, _, err := g.derivedVarCNames(n.XType()) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 | 	preName := aPrefix + n.Name().Str(g.tm) | 
 |  | 
 | 	b.printf("if (%s) {\n%s->%s = ((size_t)(%s%s - %s->data.ptr));\n}\n", | 
 | 		preName, preName, i1, iopPrefix, preName, preName) | 
 | 	return nil | 
 | } | 
 |  | 
 | func (g *gen) writeLoadDerivedVar(b *buffer, n *a.Expr) error { | 
 | 	_, i1, _, _, err := g.derivedVarCNames(n.MType()) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	switch n.Operator() { | 
 | 	case 0: | 
 | 		name := n.Ident().Str(g.tm) | 
 | 		b.printf("%s%s%s = %s%s.data.ptr + %s%s.%s;\n", | 
 | 			iopPrefix, vPrefix, name, uPrefix, name, uPrefix, name, i1) | 
 | 		return nil | 
 |  | 
 | 	case t.IDDot: | 
 | 		if lhs := n.LHS().AsExpr(); (lhs.Operator() == 0) && (lhs.Ident() == t.IDArgs) { | 
 | 			name := n.Ident().Str(g.tm) | 
 | 			b.printf("if (%s%s) {\n", aPrefix, name) | 
 | 			b.printf("%s%s%s = %s%s->data.ptr + %s%s->%s;\n", | 
 | 				iopPrefix, aPrefix, name, aPrefix, name, aPrefix, name, i1) | 
 | 			b.printf("}\n") | 
 | 			return nil | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return fmt.Errorf("could not determine derived variables for %q", n.Str(g.tm)) | 
 | } | 
 |  | 
 | func (g *gen) writeSaveDerivedVar(b *buffer, n *a.Expr) error { | 
 | 	_, i1, _, _, err := g.derivedVarCNames(n.MType()) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	switch n.Operator() { | 
 | 	case 0: | 
 | 		name := n.Ident().Str(g.tm) | 
 | 		b.printf("%s%s.%s = ((size_t)(%s%s%s - %s%s.data.ptr));\n", | 
 | 			uPrefix, name, i1, iopPrefix, vPrefix, name, uPrefix, name) | 
 | 		return nil | 
 |  | 
 | 	case t.IDDot: | 
 | 		if lhs := n.LHS().AsExpr(); (lhs.Operator() == 0) && (lhs.Ident() == t.IDArgs) { | 
 | 			name := n.Ident().Str(g.tm) | 
 | 			b.printf("if (%s%s) {\n", aPrefix, name) | 
 | 			b.printf("%s%s->%s = ((size_t)(%s%s%s - %s%s->data.ptr));\n", | 
 | 				aPrefix, name, i1, iopPrefix, aPrefix, name, aPrefix, name) | 
 | 			b.printf("}\n") | 
 | 			return nil | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return fmt.Errorf("could not determine derived variables for %q", n.Str(g.tm)) | 
 | } | 
 |  | 
 | func (g *gen) couldHaveDerivedVar(n *a.Expr) bool { | 
 | 	typ := n.MType() | 
 | 	if typ.Decorator() != 0 { | 
 | 		return false | 
 | 	} | 
 |  | 
 | 	qid := typ.QID() | 
 | 	if qid[0] != t.IDBase { | 
 | 		return false | 
 | 	} | 
 | 	switch qid[1] { | 
 | 	case t.IDIOReader, t.IDIOWriter, t.IDTokenReader, t.IDTokenWriter: | 
 | 		// No-op. | 
 | 	default: | 
 | 		return false | 
 | 	} | 
 |  | 
 | 	switch n.Operator() { | 
 | 	case t.IDDot: | 
 | 		if lhs := n.LHS().AsExpr(); (lhs.Operator() == 0) && (lhs.Ident() == t.IDArgs) { | 
 | 			if g.currFunk.derivedVars == nil { | 
 | 				return false | 
 | 			} else if _, ok := g.currFunk.derivedVars[n.Ident()]; !ok { | 
 | 				return false | 
 | 			} | 
 | 		} | 
 |  | 
 | 	case t.IDOpenParen: | 
 | 		switch n.LHS().AsExpr().Ident() { | 
 | 		case t.IDEmptyIOReader, t.IDEmptyIOWriter: | 
 | 			if n.LHS().AsExpr().LHS().AsExpr().MType().IsEtcUtilityType() { | 
 | 				return false | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return true | 
 | } | 
 |  | 
 | func (g *gen) writeLoadExprDerivedVars(b *buffer, n *a.Expr) error { | 
 | 	if (g.currFunk.derivedVars != nil) && (n.Operator() == a.ExprOperatorCall) { | 
 | 		for _, o := range n.Args() { | 
 | 			if v := o.AsArg().Value(); g.couldHaveDerivedVar(v) { | 
 | 				if err := g.writeLoadDerivedVar(b, v); err != nil { | 
 | 					return err | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | func (g *gen) writeSaveExprDerivedVars(b *buffer, n *a.Expr) error { | 
 | 	if (g.currFunk.derivedVars != nil) && (n.Operator() == a.ExprOperatorCall) { | 
 | 		for _, o := range n.Args() { | 
 | 			if v := o.AsArg().Value(); g.couldHaveDerivedVar(v) { | 
 | 				if err := g.writeSaveDerivedVar(b, v); err != nil { | 
 | 					return err | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | func (g *gen) writeResumeSuspend1(b *buffer, f *funk, n *a.Var, suspend bool) error { | 
 | 	if typ := n.XType(); typ.HasPointers() { | 
 | 		return nil | 
 | 	} else if f.varResumables == nil || !f.varResumables[n.Name()] { | 
 | 		return nil | 
 | 	} else { | 
 | 		local := fmt.Sprintf("%s%s", vPrefix, n.Name().Str(g.tm)) | 
 | 		lhs := local | 
 | 		// TODO: don't hard-code [0], and allow recursive coroutines. | 
 | 		rhs := fmt.Sprintf("self->private_data.%s%s[0].%s", sPrefix, g.currFunk.astFunc.FuncName().Str(g.tm), lhs) | 
 | 		if suspend { | 
 | 			lhs, rhs = rhs, lhs | 
 | 		} | 
 |  | 
 | 		switch typ.Decorator() { | 
 | 		case 0: | 
 | 			b.printf("%s = %s;\n", lhs, rhs) | 
 | 			return nil | 
 | 		case t.IDArray: | 
 | 			inner := typ.Inner() | 
 | 			if inner.Decorator() != 0 { | 
 | 				break | 
 | 			} | 
 | 			qid := inner.QID() | 
 | 			if qid[0] != t.IDBase { | 
 | 				break | 
 | 			} | 
 | 			switch qid[1] { | 
 | 			case t.IDU8, t.IDU16, t.IDU32, t.IDU64: | 
 | 				b.printf("memcpy(%s, %s, sizeof(%s));\n", lhs, rhs, local) | 
 | 				return nil | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return fmt.Errorf("cannot resume or suspend a local variable %q of type %q", | 
 | 		n.Name().Str(g.tm), n.XType().Str(g.tm)) | 
 | } | 
 |  | 
 | func (g *gen) writeResumeSuspend(b *buffer, f *funk, suspend bool) error { | 
 | 	for _, n := range f.varList { | 
 | 		if err := g.writeResumeSuspend1(b, f, n, suspend); err != nil { | 
 | 			return err | 
 | 		} | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | func (g *gen) writeVars(b *buffer, f *funk, inStructDecl bool) error { | 
 | 	for _, n := range f.varList { | 
 | 		typ := n.XType() | 
 | 		if typ.Innermost().IsEtcUtilityType() { | 
 | 			continue | 
 | 		} else if inStructDecl && | 
 | 			(typ.HasPointers() || f.varResumables == nil || !f.varResumables[n.Name()]) { | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		name := n.Name().Str(g.tm) | 
 |  | 
 | 		if typ.IsIOType() { | 
 | 			b.printf("wuffs_base__io_buffer %s%s = wuffs_base__empty_io_buffer();\n", uPrefix, name) | 
 | 		} else if typ.IsTokenType() { | 
 | 			return fmt.Errorf("TODO: support token_{reader,writer} typed variables") | 
 | 		} | 
 |  | 
 | 		if err := g.writeCTypeName(b, typ, vPrefix, name); err != nil { | 
 | 			return err | 
 | 		} | 
 | 		if inStructDecl { | 
 | 			b.writes(";\n") | 
 | 		} else if typ.IsNumType() { | 
 | 			b.writes(" = 0;\n") | 
 | 		} else if typ.IsBool() { | 
 | 			b.writes(" = false;\n") | 
 | 		} else if typ.IsStatus() { | 
 | 			b.writes(" = wuffs_base__make_status(NULL);\n") | 
 | 		} else if typ.IsIOType() { | 
 | 			b.printf(" = &%s%s;\n", uPrefix, name) | 
 | 		} else if typ.Eq(typeExprARMCRC32U32) { | 
 | 			b.writes(" = 0;\n") | 
 | 		} else { | 
 | 			b.writes(" = {0};\n") | 
 | 		} | 
 |  | 
 | 		if typ.IsIOType() { | 
 | 			qualifier := "" | 
 | 			if typ.QID()[1] == t.IDIOReader { | 
 | 				qualifier = "const " | 
 | 			} | 
 | 			b.printf("%suint8_t* %s%s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", | 
 | 				qualifier, iopPrefix, vPrefix, name) | 
 | 			b.printf("%suint8_t* %s%s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", | 
 | 				qualifier, io0Prefix, vPrefix, name) | 
 | 			b.printf("%suint8_t* %s%s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", | 
 | 				qualifier, io1Prefix, vPrefix, name) | 
 | 			b.printf("%suint8_t* %s%s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", | 
 | 				qualifier, io2Prefix, vPrefix, name) | 
 | 		} | 
 | 	} | 
 | 	return nil | 
 | } |