| // 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.KIOBind: |
| if p.AsIOBind().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 |
| } |