diff options
Diffstat (limited to 'vendor/github.com/ugorji/go/codec/gen_mono.go')
| -rw-r--r-- | vendor/github.com/ugorji/go/codec/gen_mono.go | 586 |
1 files changed, 586 insertions, 0 deletions
diff --git a/vendor/github.com/ugorji/go/codec/gen_mono.go b/vendor/github.com/ugorji/go/codec/gen_mono.go new file mode 100644 index 000000000..841147863 --- /dev/null +++ b/vendor/github.com/ugorji/go/codec/gen_mono.go @@ -0,0 +1,586 @@ +// Copyright (c) 2012-2020 Ugorji Nwoke. All rights reserved. +// Use of this source code is governed by a MIT license found in the LICENSE file. + +//go:build codec.build + +package codec + +import ( + "go/ast" + "go/format" + "go/parser" + "go/token" + "os" + "slices" + "strings" +) + +// This tool will monomorphize types scoped to a specific format. +// +// This tool only monomorphized the type Name, and not a function Name. +// Explicitly, generic functions are not supported, as they cannot be monomorphized +// to a specific format without a corresponding name change. +// +// However, for types constrained to encWriter or decReader, +// which are shared across formats, there's no place to put them without duplication. + +const genMonoParserMode = parser.AllErrors | parser.SkipObjectResolution + +var genMonoSpecialFieldTypes = []string{"helperDecReader"} + +// These functions should take the address of first param when monomorphized +var genMonoSpecialFunc4Addr = []string{} // {"decByteSlice"} + +var genMonoImportsToSkip = []string{`"errors"`, `"fmt"`, `"net/rpc"`} + +var genMonoRefImportsVia_ = [][2]string{ + // {"errors", "New"}, +} + +var genMonoCallsToSkip = []string{"callMake"} + +type genMonoFieldState uint + +const ( + genMonoFieldRecv genMonoFieldState = iota << 1 + genMonoFieldParamsResult + genMonoFieldStruct +) + +type genMonoImports struct { + set map[string]struct{} + specs []*ast.ImportSpec +} + +type genMono struct { + files map[string][]byte + typParam map[string]*ast.Field + typParamTransient map[string]*ast.Field +} + +func (x *genMono) init() { + x.files = make(map[string][]byte) + x.typParam = make(map[string]*ast.Field) + x.typParamTransient = make(map[string]*ast.Field) +} + +func (x *genMono) reset() { + clear(x.typParam) + clear(x.typParamTransient) +} + +func (m *genMono) hdl(hname string) { + m.reset() + m.do(hname, []string{"encode.go", "decode.go", hname + ".go"}, []string{"base.notfastpath.go", "base.notfastpath.notmono.go"}, "", "") + m.do(hname, []string{"base.notfastpath.notmono.go"}, nil, ".notfastpath", ` && (notfastpath || codec.notfastpath)`) + m.do(hname, []string{"base.fastpath.notmono.generated.go"}, []string{"base.fastpath.generated.go"}, ".fastpath", ` && !notfastpath && !codec.notfastpath`) +} + +func (m *genMono) do(hname string, fnames, tnames []string, fnameInfx string, buildTagsSfx string) { + // keep m.typParams across whole call, as all others use it + const fnameSfx = ".mono.generated.go" + fname := hname + fnameInfx + fnameSfx + + var imports = genMonoImports{set: make(map[string]struct{})} + + r1, fset := m.merge(fnames, tnames, &imports) + m.trFile(r1, hname, true) + + r2, fset := m.merge(fnames, tnames, &imports) + m.trFile(r2, hname, false) + + r0 := genMonoOutInit(imports.specs, fname) + r0.Decls = append(r0.Decls, r1.Decls...) + r0.Decls = append(r0.Decls, r2.Decls...) + + // output r1 to a file + f, err := os.Create(fname) + halt.onerror(err) + defer f.Close() + + var s genMonoStrBuilder + s.s(`//go:build !notmono && !codec.notmono `).s(buildTagsSfx).s(` + +// Copyright (c) 2012-2020 Ugorji Nwoke. All rights reserved. +// Use of this source code is governed by a MIT license found in the LICENSE file. + +`) + _, err = f.Write(s.v) + halt.onerror(err) + err = format.Node(f, fset, r0) + halt.onerror(err) +} + +func (x *genMono) file(fname string) (b []byte) { + b = x.files[fname] + if b == nil { + var err error + b, err = os.ReadFile(fname) + halt.onerror(err) + x.files[fname] = b + } + return +} + +func (x *genMono) merge(fNames, tNames []string, imports *genMonoImports) (dst *ast.File, fset *token.FileSet) { + // typParams used in fnLoadTyps + var typParams map[string]*ast.Field + var loadTyps bool + fnLoadTyps := func(node ast.Node) bool { + var ok bool + switch n := node.(type) { + case *ast.GenDecl: + if n.Tok == token.TYPE { + for _, v := range n.Specs { + nn := v.(*ast.TypeSpec) + ok = genMonoTypeParamsOk(nn.TypeParams) + if ok { + // each decl will have only 1 var/type + typParams[nn.Name.Name] = nn.TypeParams.List[0] + if loadTyps { + dst.Decls = append(dst.Decls, &ast.GenDecl{Tok: n.Tok, Specs: []ast.Spec{v}}) + } + } + } + } + return false + } + return true + } + + // we only merge top-level methods and types + fnIdX := func(n *ast.FuncDecl, n2 *ast.IndexExpr) (ok bool) { + n9, ok9 := n2.Index.(*ast.Ident) + n3, ok := n2.X.(*ast.Ident) // n3 = type name + ok = ok && ok9 && n9.Name == "T" + if ok { + _, ok = x.typParam[n3.Name] + } + return + } + + fnLoadMethodsAndImports := func(node ast.Node) bool { + var ok bool + switch n := node.(type) { + case *ast.FuncDecl: + // TypeParams is nil for methods, as it is defined at the type node + // instead, look at the name, and + // if IndexExpr.Index=T, and IndexExpr.X matches a type name seen already + // then ok = true + if n.Recv == nil || len(n.Recv.List) != 1 { + return false + } + ok = false + switch nn := n.Recv.List[0].Type.(type) { + case *ast.IndexExpr: + ok = fnIdX(n, nn) + case *ast.StarExpr: + switch nn2 := nn.X.(type) { + case *ast.IndexExpr: + ok = fnIdX(n, nn2) + } + } + if ok { + dst.Decls = append(dst.Decls, n) + } + return false + case *ast.GenDecl: + if n.Tok == token.IMPORT { + for _, v := range n.Specs { + nn := v.(*ast.ImportSpec) + if slices.Contains(genMonoImportsToSkip, nn.Path.Value) { + continue + } + if _, ok = imports.set[nn.Path.Value]; !ok { + imports.specs = append(imports.specs, nn) + imports.set[nn.Path.Value] = struct{}{} + } + } + } + return false + } + return true + } + + fset = token.NewFileSet() + fnLoadAsts := func(names []string) (asts []*ast.File) { + for _, fname := range names { + fsrc := x.file(fname) + f, err := parser.ParseFile(fset, fname, fsrc, genMonoParserMode) + halt.onerror(err) + asts = append(asts, f) + } + return + } + + clear(x.typParamTransient) + + dst = &ast.File{ + Name: &ast.Ident{Name: "codec"}, + } + + fs := fnLoadAsts(fNames) + ts := fnLoadAsts(tNames) + + loadTyps = true + typParams = x.typParam + for _, v := range fs { + ast.Inspect(v, fnLoadTyps) + } + loadTyps = false + typParams = x.typParamTransient + for _, v := range ts { + ast.Inspect(v, fnLoadTyps) + } + typParams = nil + for _, v := range fs { + ast.Inspect(v, fnLoadMethodsAndImports) + } + + return +} + +func (x *genMono) trFile(r *ast.File, hname string, isbytes bool) { + fn := func(node ast.Node) bool { + switch n := node.(type) { + case *ast.TypeSpec: + // type x[T encDriver] struct { ... } + if !genMonoTypeParamsOk(n.TypeParams) { + return false + } + x.trType(n, hname, isbytes) + return false + case *ast.FuncDecl: + if n.Recv == nil || len(n.Recv.List) != 1 { + return false + } + if _, ok := n.Recv.List[0].Type.(*ast.Ident); ok { + return false + } + tp := x.trMethodSign(n, hname, isbytes) // receiver, params, results + // handle the body + x.trMethodBody(n.Body, tp, hname, isbytes) + return false + } + return true + } + ast.Inspect(r, fn) + + // set type params to nil, and Pos to NoPos + fn = func(node ast.Node) bool { + switch n := node.(type) { + case *ast.FuncType: + if genMonoTypeParamsOk(n.TypeParams) { + n.TypeParams = nil + } + case *ast.TypeSpec: // for type ... + if genMonoTypeParamsOk(n.TypeParams) { + n.TypeParams = nil + } + } + return true + } + ast.Inspect(r, fn) +} + +func (x *genMono) trType(n *ast.TypeSpec, hname string, isbytes bool) { + sfx, _, _, hnameUp := genMonoIsBytesVals(hname, isbytes) + tp := n.TypeParams.List[0] + switch tp.Type.(*ast.Ident).Name { + case "encDriver", "decDriver": + n.Name.Name += hnameUp + sfx + case "encWriter", "decReader": + n.Name.Name += sfx + } + + // handle the Struct and Array types + switch nn := n.Type.(type) { + case *ast.StructType: + x.trStruct(nn, tp, hname, isbytes) + case *ast.ArrayType: + x.trArray(nn, tp, hname, isbytes) + } +} + +func (x *genMono) trMethodSign(n *ast.FuncDecl, hname string, isbytes bool) (tp *ast.Field) { + // check if recv type is not parameterized + tp = x.trField(n.Recv.List[0], nil, hname, isbytes, genMonoFieldRecv) + // handle params and results + x.trMethodSignNonRecv(n.Type.Params, tp, hname, isbytes) + x.trMethodSignNonRecv(n.Type.Results, tp, hname, isbytes) + return +} + +func (x *genMono) trMethodSignNonRecv(r *ast.FieldList, tp *ast.Field, hname string, isbytes bool) { + if r == nil || len(r.List) == 0 { + return + } + for _, v := range r.List { + x.trField(v, tp, hname, isbytes, genMonoFieldParamsResult) + } +} + +func (x *genMono) trStruct(r *ast.StructType, tp *ast.Field, hname string, isbytes bool) { + // search for fields, and update accordingly + // type x[T encDriver] struct { w T } + // var x *A[T] + // A[T] + if r == nil || r.Fields == nil || len(r.Fields.List) == 0 { + return + } + for _, v := range r.Fields.List { + x.trField(v, tp, hname, isbytes, genMonoFieldStruct) + } +} + +func (x *genMono) trArray(n *ast.ArrayType, tp *ast.Field, hname string, isbytes bool) { + sfx, _, _, hnameUp := genMonoIsBytesVals(hname, isbytes) + // type fastpathEs[T encDriver] [56]fastpathE[T] + // p := tp.Names[0].Name + switch elt := n.Elt.(type) { + // case *ast.InterfaceType: + case *ast.IndexExpr: + if elt.Index.(*ast.Ident).Name == "T" { // generic + n.Elt = ast.NewIdent(elt.X.(*ast.Ident).Name + hnameUp + sfx) + } + } +} + +func (x *genMono) trMethodBody(r *ast.BlockStmt, tp *ast.Field, hname string, isbytes bool) { + // find the parent node for an indexExpr, or a T/*T, and set the value back in there + + fn := func(pnode ast.Node) bool { + var pn *ast.Ident + fnUp := func() { + x.updateIdentForT(pn, hname, tp, isbytes, false) + } + switch n := pnode.(type) { + // case *ast.SelectorExpr: + // case *ast.TypeAssertExpr: + // case *ast.IndexExpr: + case *ast.StarExpr: + if genMonoUpdateIndexExprT(&pn, n.X) { + n.X = pn + fnUp() + } + case *ast.CallExpr: + for i4, n4 := range n.Args { + if genMonoUpdateIndexExprT(&pn, n4) { + n.Args[i4] = pn + fnUp() + } + } + if n4, ok4 := n.Fun.(*ast.Ident); ok4 && slices.Contains(genMonoSpecialFunc4Addr, n4.Name) { + n.Args[0] = &ast.UnaryExpr{Op: token.AND, X: n.Args[0].(*ast.SelectorExpr)} + } + case *ast.CompositeLit: + if genMonoUpdateIndexExprT(&pn, n.Type) { + n.Type = pn + fnUp() + } + case *ast.ArrayType: + if genMonoUpdateIndexExprT(&pn, n.Elt) { + n.Elt = pn + fnUp() + } + case *ast.ValueSpec: + for i2, n2 := range n.Values { + if genMonoUpdateIndexExprT(&pn, n2) { + n.Values[i2] = pn + fnUp() + } + } + if genMonoUpdateIndexExprT(&pn, n.Type) { + n.Type = pn + fnUp() + } + case *ast.BinaryExpr: + // early return here, since the 2 things can apply + if genMonoUpdateIndexExprT(&pn, n.X) { + n.X = pn + fnUp() + } + if genMonoUpdateIndexExprT(&pn, n.Y) { + n.Y = pn + fnUp() + } + return true + } + return true + } + ast.Inspect(r, fn) +} + +func (x *genMono) trField(f *ast.Field, tpt *ast.Field, hname string, isbytes bool, state genMonoFieldState) (tp *ast.Field) { + var pn *ast.Ident + switch nn := f.Type.(type) { + case *ast.IndexExpr: + if genMonoUpdateIndexExprT(&pn, nn) { + f.Type = pn + } + case *ast.StarExpr: + if genMonoUpdateIndexExprT(&pn, nn.X) { + nn.X = pn + } + case *ast.FuncType: + x.trMethodSignNonRecv(nn.Params, tpt, hname, isbytes) + x.trMethodSignNonRecv(nn.Results, tpt, hname, isbytes) + return + case *ast.ArrayType: + x.trArray(nn, tpt, hname, isbytes) + return + case *ast.Ident: + if state == genMonoFieldRecv || nn.Name != "T" { + return + } + pn = nn // "T" + if state == genMonoFieldParamsResult { + f.Type = &ast.StarExpr{X: pn} + } + } + if pn == nil { + return + } + + tp = x.updateIdentForT(pn, hname, tpt, isbytes, true) + return +} + +func (x *genMono) updateIdentForT(pn *ast.Ident, hname string, tp *ast.Field, + isbytes bool, lookupTP bool) (tp2 *ast.Field) { + sfx, writer, reader, hnameUp := genMonoIsBytesVals(hname, isbytes) + // handle special ones e.g. helperDecReader et al + if slices.Contains(genMonoSpecialFieldTypes, pn.Name) { + pn.Name += sfx + return + } + + if pn.Name != "T" && lookupTP { + tp = x.typParam[pn.Name] + if tp == nil { + tp = x.typParamTransient[pn.Name] + } + } + + paramtyp := tp.Type.(*ast.Ident).Name + if pn.Name == "T" { + switch paramtyp { + case "encDriver", "decDriver": + pn.Name = hname + genMonoTitleCase(paramtyp) + sfx + case "encWriter": + pn.Name = writer + case "decReader": + pn.Name = reader + } + } else { + switch paramtyp { + case "encDriver", "decDriver": + pn.Name += hnameUp + sfx + case "encWriter", "decReader": + pn.Name += sfx + } + } + return tp +} + +func genMonoUpdateIndexExprT(pn **ast.Ident, node ast.Node) (pnok bool) { + *pn = nil + if n2, ok := node.(*ast.IndexExpr); ok { + n9, ok9 := n2.Index.(*ast.Ident) + n3, ok := n2.X.(*ast.Ident) + if ok && ok9 && n9.Name == "T" { + *pn, pnok = ast.NewIdent(n3.Name), true + } + } + return +} + +func genMonoTitleCase(s string) string { + return strings.ToUpper(s[:1]) + s[1:] +} + +func genMonoIsBytesVals(hName string, isbytes bool) (suffix, writer, reader, hNameUp string) { + hNameUp = genMonoTitleCase(hName) + if isbytes { + return "Bytes", "bytesEncAppender", "bytesDecReader", hNameUp + } + return "IO", "bufioEncWriter", "ioDecReader", hNameUp +} + +func genMonoTypeParamsOk(v *ast.FieldList) (ok bool) { + if v == nil || v.List == nil || len(v.List) != 1 { + return false + } + pn := v.List[0] + if len(pn.Names) != 1 { + return false + } + pnName := pn.Names[0].Name + if pnName != "T" { + return false + } + // ignore any nodes which are not idents e.g. cmp.orderedRv + vv, ok := pn.Type.(*ast.Ident) + if !ok { + return false + } + switch vv.Name { + case "encDriver", "decDriver", "encWriter", "decReader": + return true + } + return false +} + +func genMonoCopy(src *ast.File) (dst *ast.File) { + dst = &ast.File{ + Name: &ast.Ident{Name: "codec"}, + } + dst.Decls = append(dst.Decls, src.Decls...) + return +} + +type genMonoStrBuilder struct { + v []byte +} + +func (x *genMonoStrBuilder) s(v string) *genMonoStrBuilder { + x.v = append(x.v, v...) + return x +} + +func genMonoOutInit(importSpecs []*ast.ImportSpec, fname string) (f *ast.File) { + // ParseFile seems to skip the //go:build stanza + // it should be written directly into the file + var s genMonoStrBuilder + s.s(` +package codec + +import ( +`) + for _, v := range importSpecs { + s.s("\t").s(v.Path.Value).s("\n") + } + s.s(")\n") + for _, v := range genMonoRefImportsVia_ { + s.s("var _ = ").s(v[0]).s(".").s(v[1]).s("\n") + } + f, err := parser.ParseFile(token.NewFileSet(), fname, s.v, genMonoParserMode) + halt.onerror(err) + return +} + +func genMonoAll() { + // hdls := []Handle{ + // (*SimpleHandle)(nil), + // (*JsonHandle)(nil), + // (*CborHandle)(nil), + // (*BincHandle)(nil), + // (*MsgpackHandle)(nil), + // } + hdls := []string{"simple", "json", "cbor", "binc", "msgpack"} + var m genMono + m.init() + for _, v := range hdls { + m.hdl(v) + } +} |
