123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122 |
- package generators
- import (
- "fmt"
- "io"
- "path/filepath"
- "sort"
- "strings"
- "go-common/app/tool/gengo/args"
- "go-common/app/tool/gengo/cmd/deepcopy-gen/sets"
- "go-common/app/tool/gengo/generator"
- "go-common/app/tool/gengo/namer"
- "go-common/app/tool/gengo/types"
- "github.com/golang/glog"
- )
- // CustomArgs is used tby the go2idl framework to pass args specific to this
- // generator.
- type CustomArgs struct {
- BoundingDirs args.StringSliceVar // Only deal with types rooted under these dirs.
- }
- // This is the comment tag that carries parameters for deep-copy generation.
- const (
- tagName = "bili:deepcopy-gen"
- interfacesTagName = tagName + ":interfaces"
- structsTagName = tagName + ":structs"
- interfacesNonPointerTagName = tagName + ":nonpointer-interfaces" // attach the DeepCopy<Interface> methods to the
- )
- // Known values for the comment tag.
- const tagValuePackage = "package"
- // tagValue holds parameters from a tagName tag.
- type tagValue struct {
- value string
- register bool
- }
- func extractTag(comments []string) *tagValue {
- tagVals := types.ExtractCommentTags("+", comments)[tagName]
- if tagVals == nil {
- // No match for the tag.
- return nil
- }
- // If there are multiple values, abort.
- if len(tagVals) > 1 {
- glog.Fatalf("Found %d %s tags: %q", len(tagVals), tagName, tagVals)
- }
- // If we got here we are returning something.
- tag := &tagValue{}
- // Get the primary value.
- parts := strings.Split(tagVals[0], ",")
- if len(parts) >= 1 {
- tag.value = parts[0]
- }
- // Parse extra arguments.
- parts = parts[1:]
- for i := range parts {
- kv := strings.SplitN(parts[i], "=", 2)
- k := kv[0]
- v := ""
- if len(kv) == 2 {
- v = kv[1]
- }
- switch k {
- case "register":
- if v != "false" {
- tag.register = true
- }
- default:
- glog.Fatalf("Unsupported %s param: %q", tagName, parts[i])
- }
- }
- return tag
- }
- // TODO: This is created only to reduce number of changes in a single PR.
- // Remove it and use PublicNamer instead.
- func deepCopyNamer() *namer.NameStrategy {
- return &namer.NameStrategy{
- Join: func(pre string, in []string, post string) string {
- return strings.Join(in, "_")
- },
- PrependPackageNames: 1,
- }
- }
- // NameSystems returns the name system used by the generators in this package.
- func NameSystems() namer.NameSystems {
- return namer.NameSystems{
- "public": deepCopyNamer(),
- "raw": namer.NewRawNamer("", nil),
- }
- }
- // DefaultNameSystem returns the default name system for ordering the types to be
- // processed by the generators in this package.
- func DefaultNameSystem() string {
- return "public"
- }
- // Packages is
- func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
- boilerplate, err := arguments.LoadGoBoilerplate()
- if err != nil {
- glog.Fatalf("Failed loading boilerplate: %v", err)
- }
- inputs := sets.NewString(context.Inputs...)
- packages := generator.Packages{}
- header := append([]byte(fmt.Sprintf("// +build !%s\n\n", arguments.GeneratedBuildTag)), boilerplate...)
- boundingDirs := []string{}
- if customArgs, ok := arguments.CustomArgs.(*CustomArgs); ok {
- if customArgs.BoundingDirs == nil {
- customArgs.BoundingDirs = context.Inputs
- }
- for i := range customArgs.BoundingDirs {
- // Strip any trailing slashes - they are not exactly "correct" but
- // this is friendlier.
- boundingDirs = append(boundingDirs, strings.TrimRight(customArgs.BoundingDirs[i], "/"))
- }
- }
- for i := range inputs {
- glog.V(5).Infof("Considering pkg %q", i)
- pkg := context.Universe[i]
- if pkg == nil {
- // If the input had no Go files, for example.
- continue
- }
- ptag := extractTag(pkg.Comments)
- ptagValue := ""
- ptagRegister := false
- if ptag != nil {
- ptagValue = ptag.value
- if ptagValue != tagValuePackage {
- glog.Fatalf("Package %v: unsupported %s value: %q", i, tagName, ptagValue)
- }
- ptagRegister = ptag.register
- glog.V(5).Infof(" tag.value: %q, tag.register: %t", ptagValue, ptagRegister)
- } else {
- glog.V(5).Infof(" no tag")
- }
- // If the pkg-scoped tag says to generate, we can skip scanning types.
- pkgNeedsGeneration := (ptagValue == tagValuePackage)
- if !pkgNeedsGeneration {
- // If the pkg-scoped tag did not exist, scan all types for one that
- // explicitly wants generation.
- for _, t := range pkg.Types {
- glog.V(5).Infof(" considering type %q", t.Name.String())
- ttag := extractTag(t.CommentLines)
- if ttag != nil && ttag.value == "true" {
- glog.V(5).Infof(" tag=true")
- if !copyableType(t) {
- glog.Fatalf("Type %v requests deepcopy generation but is not copyable", t)
- }
- pkgNeedsGeneration = true
- break
- }
- }
- }
- if pkgNeedsGeneration {
- glog.V(3).Infof("Package %q needs generation", i)
- path := pkg.Path
- // if the source path is within a /vendor/ directory (for example,
- // k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/apis/meta/v1), allow
- // generation to output to the proper relative path (under vendor).
- // Otherwise, the generator will create the file in the wrong location
- // in the output directory.
- // TODO: build a more fundamental concept in gengo for dealing with modifications
- // to vendored packages.
- if strings.HasPrefix(pkg.SourcePath, arguments.OutputBase) {
- expandedPath := strings.TrimPrefix(pkg.SourcePath, arguments.OutputBase)
- if strings.Contains(expandedPath, "/vendor/") {
- path = expandedPath
- }
- }
- packages = append(packages,
- &generator.DefaultPackage{
- PackageName: strings.Split(filepath.Base(pkg.Path), ".")[0],
- PackagePath: path,
- HeaderText: header,
- GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
- return []generator.Generator{
- NewGenDeepCopy(arguments.OutputFileBaseName, pkg.Path, boundingDirs, (ptagValue == tagValuePackage), ptagRegister),
- }
- },
- FilterFunc: func(c *generator.Context, t *types.Type) bool {
- return t.Name.Package == pkg.Path
- },
- })
- }
- }
- return packages
- }
- // genDeepCopy produces a file with autogenerated deep-copy functions.
- type genDeepCopy struct {
- generator.DefaultGen
- targetPackage string
- boundingDirs []string
- allTypes bool
- registerTypes bool
- imports namer.ImportTracker
- typesForInit []*types.Type
- }
- // NewGenDeepCopy is
- func NewGenDeepCopy(sanitizedName, targetPackage string, boundingDirs []string, allTypes, registerTypes bool) generator.Generator {
- return &genDeepCopy{
- DefaultGen: generator.DefaultGen{
- OptionalName: sanitizedName,
- },
- targetPackage: targetPackage,
- boundingDirs: boundingDirs,
- allTypes: allTypes,
- registerTypes: registerTypes,
- imports: generator.NewImportTracker(),
- typesForInit: make([]*types.Type, 0),
- }
- }
- func (g *genDeepCopy) Namers(c *generator.Context) namer.NameSystems {
- // Have the raw namer for this file track what it imports.
- return namer.NameSystems{
- "raw": namer.NewRawNamer(g.targetPackage, g.imports),
- }
- }
- func (g *genDeepCopy) Filter(c *generator.Context, t *types.Type) bool {
- // Filter out types not being processed or not copyable within the package.
- enabled := g.allTypes
- if !enabled {
- ttag := extractTag(t.CommentLines)
- if ttag != nil && ttag.value == "true" {
- enabled = true
- }
- }
- if !enabled {
- return false
- }
- if !copyableType(t) {
- glog.V(2).Infof("Type %v is not copyable", t)
- return false
- }
- glog.V(4).Infof("Type %v is copyable", t)
- g.typesForInit = append(g.typesForInit, t)
- return true
- }
- func (g *genDeepCopy) copyableAndInBounds(t *types.Type) bool {
- if !copyableType(t) {
- return false
- }
- // Only packages within the restricted range can be processed.
- if !isRootedUnder(t.Name.Package, g.boundingDirs) {
- return false
- }
- return true
- }
- // deepCopyMethod returns the signature of a DeepCopy() method, nil or an error
- // if the type does not match. This allows more efficient deep copy
- // implementations to be defined by the type's author. The correct signature
- // for a type T is:
- // func (t T) DeepCopy() T
- // or:
- // func (t *T) DeepCopy() *T
- func deepCopyMethod(t *types.Type) (*types.Signature, error) {
- f, found := t.Methods["DeepCopy"]
- if !found {
- return nil, nil
- }
- if len(f.Signature.Parameters) != 0 {
- return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected no parameters", t)
- }
- if len(f.Signature.Results) != 1 {
- return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected exactly one result", t)
- }
- ptrResult := f.Signature.Results[0].Kind == types.Pointer && f.Signature.Results[0].Elem.Name == t.Name
- nonPtrResult := f.Signature.Results[0].Name == t.Name
- if !ptrResult && !nonPtrResult {
- return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected to return %s or *%s", t, t.Name.Name, t.Name.Name)
- }
- ptrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Kind == types.Pointer && f.Signature.Receiver.Elem.Name == t.Name
- nonPtrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Name == t.Name
- if ptrRcvr && !ptrResult {
- return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected a *%s result for a *%s receiver", t, t.Name.Name, t.Name.Name)
- }
- if nonPtrRcvr && !nonPtrResult {
- return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected a %s result for a %s receiver", t, t.Name.Name, t.Name.Name)
- }
- return f.Signature, nil
- }
- // deepCopyAsMethod returns the signature of a DeepCopy() method, nil or an error
- // if the type does not match. This allows more efficient deep copy
- // implementations to be defined by the type's author. The correct signature
- // for a type T is:
- // func (in T) DeepCopyAsU() U
- // or:
- // func (in *T) DeepCopyAsU() *U
- func deepCopyAsMethod(in *types.Type, out *types.Type) (*types.Signature, error) {
- asName := out.Name
- method := fmt.Sprintf("DeepCopyAs%s", asName.Name)
- f, found := in.Methods[method]
- if !found {
- return nil, nil
- }
- if len(f.Signature.Parameters) != 0 {
- return nil, fmt.Errorf("type %v: invalid %s signature, expected no parameters", in, method)
- }
- if len(f.Signature.Results) != 1 {
- return nil, fmt.Errorf("type %v: invalid %s signature, expected exactly one result", in, method)
- }
- ptrResult := f.Signature.Results[0].Kind == types.Pointer && f.Signature.Results[0].Elem.Name == out.Name
- nonPtrResult := f.Signature.Results[0].Name == out.Name
- if !ptrResult && !nonPtrResult {
- return nil, fmt.Errorf("type %v: invalid %s signature, expected to return %s or *%s", in, method, in.Name.Name, in.Name.Name)
- }
- ptrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Kind == types.Pointer && f.Signature.Receiver.Elem.Name == in.Name
- nonPtrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Name == in.Name
- if ptrRcvr && !ptrResult {
- return nil, fmt.Errorf("type %v: invalid %s signature, expected a *%s result", in, method, out.Name.Name)
- }
- if nonPtrRcvr && !nonPtrResult {
- return nil, fmt.Errorf("type %v: invalid %s signature, expected a %s result", in, method, out.Name.Name)
- }
- return f.Signature, nil
- }
- // deepCopyMethodOrDie returns the signatrue of a DeepCopy method, nil or calls glog.Fatalf
- // if the type does not match.
- func deepCopyMethodOrDie(t *types.Type) *types.Signature {
- ret, err := deepCopyMethod(t)
- if err != nil {
- glog.Fatal(err)
- }
- return ret
- }
- func deepCopyAsMethodOrDie(in *types.Type, out *types.Type) *types.Signature {
- ret, err := deepCopyAsMethod(in, out)
- if err != nil {
- glog.Fatal(err)
- }
- return ret
- }
- // deepCopyIntoMethod returns the signature of a DeepCopyInto() method, nil or an error
- // if the type is wrong. DeepCopyInto allows more efficient deep copy
- // implementations to be defined by the type's author. The correct signature
- // for a type T is:
- // func (t T) DeepCopyInto(t *T)
- // or:
- // func (t *T) DeepCopyInto(t *T)
- func deepCopyIntoMethod(t *types.Type) (*types.Signature, error) {
- f, found := t.Methods["DeepCopyInto"]
- if !found {
- return nil, nil
- }
- if len(f.Signature.Parameters) != 1 {
- return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected exactly one parameter", t)
- }
- if len(f.Signature.Results) != 0 {
- return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected no result type", t)
- }
- ptrParam := f.Signature.Parameters[0].Kind == types.Pointer && f.Signature.Parameters[0].Elem.Name == t.Name
- if !ptrParam {
- return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected parameter of type *%s", t, t.Name.Name)
- }
- ptrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Kind == types.Pointer && f.Signature.Receiver.Elem.Name == t.Name
- nonPtrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Name == t.Name
- if !ptrRcvr && !nonPtrRcvr {
- // this should never happen
- return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected a receiver of type %s or *%s", t, t.Name.Name, t.Name.Name)
- }
- return f.Signature, nil
- }
- // deepCopyIntoMethodOrDie returns the signature of a DeepCopyInto() method, nil or calls glog.Fatalf
- // if the type is wrong.
- func deepCopyIntoMethodOrDie(t *types.Type) *types.Signature {
- ret, err := deepCopyIntoMethod(t)
- if err != nil {
- glog.Fatal(err)
- }
- return ret
- }
- func isRootedUnder(pkg string, roots []string) bool {
- // Add trailing / to avoid false matches, e.g. foo/bar vs foo/barn. This
- // assumes that bounding dirs do not have trailing slashes.
- pkg = pkg + "/"
- for _, root := range roots {
- if strings.HasPrefix(pkg, root+"/") {
- return true
- }
- }
- return false
- }
- func copyableType(t *types.Type) bool {
- // If the type opts out of copy-generation, stop.
- ttag := extractTag(t.CommentLines)
- if ttag != nil && ttag.value == "false" {
- return false
- }
- // Filter out private types.
- if namer.IsPrivateGoName(t.Name.Name) {
- return false
- }
- if t.Kind == types.Alias {
- // if the underlying built-in is not deepcopy-able, deepcopy is opt-in through definition of custom methods.
- // Note that aliases of builtins, maps, slices can have deepcopy methods.
- if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil {
- return true
- } else {
- return t.Underlying.Kind != types.Builtin || copyableType(t.Underlying)
- }
- }
- if t.Kind != types.Struct {
- return false
- }
- return true
- }
- func underlyingType(t *types.Type) *types.Type {
- for t.Kind == types.Alias {
- t = t.Underlying
- }
- return t
- }
- func (g *genDeepCopy) isOtherPackage(pkg string) bool {
- if pkg == g.targetPackage {
- return false
- }
- if strings.HasSuffix(pkg, "\""+g.targetPackage+"\"") {
- return false
- }
- return true
- }
- func (g *genDeepCopy) Imports(c *generator.Context) (imports []string) {
- importLines := []string{}
- for _, singleImport := range g.imports.ImportLines() {
- if g.isOtherPackage(singleImport) {
- importLines = append(importLines, singleImport)
- }
- }
- return importLines
- }
- func argsFromType(ts ...*types.Type) generator.Args {
- a := generator.Args{
- "type": ts[0],
- }
- for i, t := range ts {
- a[fmt.Sprintf("type%d", i+1)] = t
- }
- return a
- }
- func (g *genDeepCopy) Init(c *generator.Context, w io.Writer) error {
- return nil
- }
- func (g *genDeepCopy) needsGeneration(t *types.Type) bool {
- tag := extractTag(t.CommentLines)
- tv := ""
- if tag != nil {
- tv = tag.value
- if tv != "true" && tv != "false" {
- glog.Fatalf("Type %v: unsupported %s value: %q", t, tagName, tag.value)
- }
- }
- if g.allTypes && tv == "false" {
- // The whole package is being generated, but this type has opted out.
- glog.V(5).Infof("Not generating for type %v because type opted out", t)
- return false
- }
- if !g.allTypes && tv != "true" {
- // The whole package is NOT being generated, and this type has NOT opted in.
- glog.V(5).Infof("Not generating for type %v because type did not opt in", t)
- return false
- }
- return true
- }
- func extractInterfacesTag(comments []string) []string {
- var result []string
- values := types.ExtractCommentTags("+", comments)[interfacesTagName]
- for _, v := range values {
- if len(v) == 0 {
- continue
- }
- intfs := strings.Split(v, ",")
- for _, intf := range intfs {
- if intf == "" {
- continue
- }
- result = append(result, intf)
- }
- }
- return result
- }
- func extractNonPointerInterfaces(comments []string) (bool, error) {
- values := types.ExtractCommentTags("+", comments)[interfacesNonPointerTagName]
- if len(values) == 0 {
- return false, nil
- }
- result := values[0] == "true"
- for _, v := range values {
- if v == "true" != result {
- return false, fmt.Errorf("contradicting %v value %q found to previous value %v", interfacesNonPointerTagName, v, result)
- }
- }
- return result, nil
- }
- func (g *genDeepCopy) deepCopyableInterfacesInner(c *generator.Context, t *types.Type) ([]*types.Type, error) {
- if t.Kind != types.Struct {
- return nil, nil
- }
- intfs := extractInterfacesTag(append(t.SecondClosestCommentLines, t.CommentLines...))
- var ts []*types.Type
- for _, intf := range intfs {
- t := types.ParseFullyQualifiedName(intf)
- c.AddDir(t.Package)
- intfT := c.Universe.Type(t)
- if intfT == nil {
- return nil, fmt.Errorf("unknown type %q in %s tag of type %s", intf, interfacesTagName, intfT)
- }
- if intfT.Kind != types.Interface {
- return nil, fmt.Errorf("type %q in %s tag of type %s is not an interface, but: %q", intf, interfacesTagName, t, intfT.Kind)
- }
- g.imports.AddType(intfT)
- ts = append(ts, intfT)
- }
- return ts, nil
- }
- // deepCopyableInterfaces returns the interface types to implement and whether they apply to a non-pointer receiver.
- func (g *genDeepCopy) deepCopyableInterfaces(c *generator.Context, t *types.Type) ([]*types.Type, bool, error) {
- ts, err := g.deepCopyableInterfacesInner(c, t)
- if err != nil {
- return nil, false, err
- }
- set := map[string]*types.Type{}
- for _, t := range ts {
- set[t.String()] = t
- }
- result := []*types.Type{}
- for _, t := range set {
- result = append(result, t)
- }
- TypeSlice(result).Sort() // we need a stable sorting because it determines the order in generation
- nonPointerReceiver, err := extractNonPointerInterfaces(append(t.SecondClosestCommentLines, t.CommentLines...))
- if err != nil {
- return nil, false, err
- }
- return result, nonPointerReceiver, nil
- }
- func (g *genDeepCopy) deepCopyableStructs(c *generator.Context, t *types.Type) ([]*types.Type, error) {
- if t.Kind != types.Struct {
- return nil, nil
- }
- stcs := extractStructsTag(append(t.SecondClosestCommentLines, t.CommentLines...))
- var ts []*types.Type
- for _, stc := range stcs {
- t := types.ParseFullyQualifiedName(stc)
- c.AddDir(t.Package)
- stcT := c.Universe.Type(t)
- if stcT == nil {
- return nil, fmt.Errorf("unknown type %q in %s tag of type %s", stc, structsTagName, stcT)
- }
- if stcT.Kind != types.Struct {
- return nil, fmt.Errorf("type %q in %s tag of type %s is not an struct, but: %q", stc, structsTagName, t, stcT.Kind)
- }
- g.imports.AddType(stcT)
- ts = append(ts, stcT)
- }
- set := map[string]*types.Type{}
- for _, t := range ts {
- set[t.String()] = t
- }
- result := []*types.Type{}
- for _, t := range set {
- result = append(result, t)
- }
- TypeSlice(result).Sort() // we need a stable sorting because it determines the order in generation
- return result, nil
- }
- func extractStructsTag(comments []string) []string {
- var result []string
- values := types.ExtractCommentTags("+", comments)[structsTagName]
- for _, v := range values {
- if len(v) == 0 {
- continue
- }
- stcs := strings.Split(v, ",")
- for _, stc := range stcs {
- if stc == "" {
- continue
- }
- result = append(result, stc)
- }
- }
- return result
- }
- // TypeSlice is
- type TypeSlice []*types.Type
- func (s TypeSlice) Len() int { return len(s) }
- func (s TypeSlice) Less(i, j int) bool { return s[i].String() < s[j].String() }
- func (s TypeSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
- func (s TypeSlice) Sort() { sort.Sort(s) }
- func (g *genDeepCopy) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
- if !g.needsGeneration(t) {
- return nil
- }
- glog.V(5).Infof("Generating deepcopy function for type %v", t)
- sw := generator.NewSnippetWriter(w, c, "$", "$")
- args := argsFromType(t)
- if deepCopyIntoMethodOrDie(t) == nil {
- sw.Do("// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\n", args)
- if isReference(t) {
- sw.Do("func (in $.type|raw$) DeepCopyInto(out *$.type|raw$) {\n", args)
- sw.Do("{in:=&in\n", nil)
- } else {
- sw.Do("func (in *$.type|raw$) DeepCopyInto(out *$.type|raw$) {\n", args)
- }
- if deepCopyMethodOrDie(t) != nil {
- if t.Methods["DeepCopy"].Signature.Receiver.Kind == types.Pointer {
- sw.Do("clone := in.DeepCopy()\n", nil)
- sw.Do("*out = *clone\n", nil)
- } else {
- sw.Do("*out = in.DeepCopy()\n", nil)
- }
- sw.Do("return\n", nil)
- } else {
- g.generateFor(t, sw)
- sw.Do("return\n", nil)
- }
- if isReference(t) {
- sw.Do("}\n", nil)
- }
- sw.Do("}\n\n", nil)
- }
- if deepCopyMethodOrDie(t) == nil {
- sw.Do("// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new $.type|raw$.\n", args)
- if isReference(t) {
- sw.Do("func (in $.type|raw$) DeepCopy() $.type|raw$ {\n", args)
- } else {
- sw.Do("func (in *$.type|raw$) DeepCopy() *$.type|raw$ {\n", args)
- }
- sw.Do("if in == nil { return nil }\n", nil)
- sw.Do("out := new($.type|raw$)\n", args)
- sw.Do("in.DeepCopyInto(out)\n", nil)
- if isReference(t) {
- sw.Do("return *out\n", nil)
- } else {
- sw.Do("return out\n", nil)
- }
- sw.Do("}\n\n", nil)
- }
- intfs, nonPointerReceiver, err := g.deepCopyableInterfaces(c, t)
- if err != nil {
- return err
- }
- for _, intf := range intfs {
- sw.Do(fmt.Sprintf("// DeepCopy%s is an autogenerated deepcopy function, copying the receiver, creating a new $.type2|raw$.\n", intf.Name.Name), argsFromType(t, intf))
- if nonPointerReceiver {
- sw.Do(fmt.Sprintf("func (in $.type|raw$) DeepCopy%s() $.type2|raw$ {\n", intf.Name.Name), argsFromType(t, intf))
- sw.Do("return *in.DeepCopy()", nil)
- sw.Do("}\n\n", nil)
- } else {
- sw.Do(fmt.Sprintf("func (in *$.type|raw$) DeepCopy%s() $.type2|raw$ {\n", intf.Name.Name), argsFromType(t, intf))
- sw.Do("if c := in.DeepCopy(); c != nil {\n", nil)
- sw.Do("return c\n", nil)
- sw.Do("}\n", nil)
- sw.Do("return nil\n", nil)
- sw.Do("}\n\n", nil)
- }
- }
- stcs, err := g.deepCopyableStructs(c, t)
- if err != nil {
- return err
- }
- for _, stc := range stcs {
- sw.Do(fmt.Sprintf("// DeepCopyAsInto%s is an autogenerated deepcopy function, copying the receiver, writing into $.type2|raw$.\n", stc.Name.Name), argsFromType(t, stc))
- sw.Do(fmt.Sprintf("func (in *$.type|raw$) DeepCopyAsInto%s(out *$.type2|raw$) {\n", stc.Name.Name), argsFromType(t, stc))
- g.doCrossTypeStruct(t, stc, sw)
- sw.Do("return\n", nil)
- sw.Do("}\n\n", nil)
- }
- for _, stc := range stcs {
- sw.Do(fmt.Sprintf("// DeepCopyFrom%s is an autogenerated deepcopy function, copying the receiver, writing into $.type2|raw$.\n", stc.Name.Name), argsFromType(t, stc))
- sw.Do(fmt.Sprintf("func (out *$.type|raw$) DeepCopyFrom%s(in *$.type2|raw$) {\n", stc.Name.Name), argsFromType(t, stc))
- g.doCrossTypeStruct(stc, t, sw)
- sw.Do("return\n", nil)
- sw.Do("}\n\n", nil)
- }
- for _, stc := range stcs {
- ut := underlyingType(t)
- stcUt := underlyingType(stc)
- if deepCopyAsMethodOrDie(ut, stcUt) != nil {
- glog.V(5).Infof("findDeepCopyAsMethod for %v and %v", ut, stcUt)
- continue
- }
- sw.Do(fmt.Sprintf("// DeepCopyAs%s is an autogenerated deepcopy function, copying the receiver, creating a new $.type2|raw$.\n", stc.Name.Name), argsFromType(t, stc))
- sw.Do(fmt.Sprintf("func (in *$.type|raw$) DeepCopyAs%s() *$.type2|raw$ {\n", stc.Name.Name), argsFromType(t, stc))
- sw.Do("if in == nil { return nil }\n", nil)
- sw.Do("out := new($.type2|raw$)\n", argsFromType(t, stc))
- sw.Do(fmt.Sprintf("in.DeepCopyAsInto%s(out)\n", stc.Name.Name), nil)
- sw.Do(fmt.Sprintf("return out\n"), nil)
- sw.Do("}\n\n", nil)
- }
- return sw.Error()
- }
- // isReference return true for pointer, maps, slices and aliases of those.
- func isReference(t *types.Type) bool {
- if t.Kind == types.Pointer || t.Kind == types.Map || t.Kind == types.Slice {
- return true
- }
- return t.Kind == types.Alias && isReference(underlyingType(t))
- }
- // we use the system of shadowing 'in' and 'out' so that the same code is valid
- // at any nesting level. This makes the autogenerator easy to understand, and
- // the compiler shouldn't care.
- func (g *genDeepCopy) generateFor(t *types.Type, sw *generator.SnippetWriter) {
- // derive inner types if t is an alias. We call the do* methods below with the alias type.
- // basic rule: generate according to inner type, but construct objects with the alias type.
- ut := underlyingType(t)
- var f func(*types.Type, *generator.SnippetWriter)
- switch ut.Kind {
- case types.Builtin:
- f = g.doBuiltin
- case types.Map:
- f = g.doMap
- case types.Slice:
- f = g.doSlice
- case types.Struct:
- f = g.doStruct
- case types.Pointer:
- f = g.doPointer
- case types.Interface:
- // interfaces are handled in-line in the other cases
- glog.Fatalf("Hit an interface type %v. This should never happen.", t)
- case types.Alias:
- // can never happen because we branch on the underlying type which is never an alias
- glog.Fatalf("Hit an alias type %v. This should never happen.", t)
- default:
- glog.Fatalf("Hit an unsupported type %v.", t)
- }
- f(t, sw)
- }
- // doBuiltin generates code for a builtin or an alias to a builtin. The generated code is
- // is the same for both cases, i.e. it's the code for the underlying type.
- func (g *genDeepCopy) doBuiltin(t *types.Type, sw *generator.SnippetWriter) {
- if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil {
- sw.Do("*out = in.DeepCopy()\n", nil)
- return
- }
- sw.Do("*out = *in\n", nil)
- }
- func (g *genDeepCopy) doBuiltinMember(m *types.Member, sw *generator.SnippetWriter) {
- t := m.Type
- if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil {
- // sw.Do("*out = in.DeepCopy()\n", nil)
- sw.Do(fmt.Sprintf("out.%s = in.DeepCopy()\n", m.Name), nil)
- return
- }
- sw.Do(fmt.Sprintf("out.%s = in.%s\n", m.Name, m.Name), nil)
- }
- // doMap generates code for a map or an alias to a map. The generated code is
- // is the same for both cases, i.e. it's the code for the underlying type.
- func (g *genDeepCopy) doMap(t *types.Type, sw *generator.SnippetWriter) {
- ut := underlyingType(t)
- uet := underlyingType(ut.Elem)
- if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil {
- sw.Do("*out = in.DeepCopy()\n", nil)
- return
- }
- if !ut.Key.IsAssignable() {
- glog.Fatalf("Hit an unsupported type %v.", uet)
- }
- sw.Do("*out = make($.|raw$, len(*in))\n", t)
- sw.Do("for key, val := range *in {\n", nil)
- dc, dci := deepCopyMethodOrDie(ut.Elem), deepCopyIntoMethodOrDie(ut.Elem)
- switch {
- case dc != nil || dci != nil:
- // Note: a DeepCopy exists because it is added if DeepCopyInto is manually defined
- leftPointer := ut.Elem.Kind == types.Pointer
- rightPointer := !isReference(ut.Elem)
- if dc != nil {
- rightPointer = dc.Results[0].Kind == types.Pointer
- }
- if leftPointer == rightPointer {
- sw.Do("(*out)[key] = val.DeepCopy()\n", nil)
- } else if leftPointer {
- sw.Do("x := val.DeepCopy()\n", nil)
- sw.Do("(*out)[key] = &x\n", nil)
- } else {
- sw.Do("(*out)[key] = *val.DeepCopy()\n", nil)
- }
- case ut.Elem.IsAnonymousStruct(): // not uet here because it needs type cast
- sw.Do("(*out)[key] = val\n", nil)
- case uet.IsAssignable():
- sw.Do("(*out)[key] = val\n", nil)
- case uet.Kind == types.Interface:
- sw.Do("if val == nil {(*out)[key]=nil} else {\n", nil)
- // Note: if t.Elem has been an alias "J" of an interface "I" in Go, we will see it
- // as kind Interface of name "J" here, i.e. generate val.DeepCopyJ(). The golang
- // parser does not give us the underlying interface name. So we cannot do any better.
- sw.Do(fmt.Sprintf("(*out)[key] = val.DeepCopy%s()\n", uet.Name.Name), nil)
- sw.Do("}\n", nil)
- case uet.Kind == types.Slice || uet.Kind == types.Map || uet.Kind == types.Pointer:
- sw.Do("var outVal $.|raw$\n", uet)
- sw.Do("if val == nil { (*out)[key] = nil } else {\n", nil)
- sw.Do("in, out := &val, &outVal\n", uet)
- g.generateFor(ut.Elem, sw)
- sw.Do("}\n", nil)
- sw.Do("(*out)[key] = outVal\n", nil)
- case uet.Kind == types.Struct:
- sw.Do("(*out)[key] = *val.DeepCopy()\n", uet)
- default:
- glog.Fatalf("Hit an unsupported type %v.", uet)
- }
- sw.Do("}\n", nil)
- }
- // doSlice generates code for a slice or an alias to a slice. The generated code is
- // is the same for both cases, i.e. it's the code for the underlying type.
- func (g *genDeepCopy) doSlice(t *types.Type, sw *generator.SnippetWriter) {
- ut := underlyingType(t)
- uet := underlyingType(ut.Elem)
- if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil {
- sw.Do("*out = in.DeepCopy()\n", nil)
- return
- }
- sw.Do("*out = make($.|raw$, len(*in))\n", t)
- if deepCopyMethodOrDie(ut.Elem) != nil || deepCopyIntoMethodOrDie(ut.Elem) != nil {
- sw.Do("for i := range *in {\n", nil)
- // Note: a DeepCopyInto exists because it is added if DeepCopy is manually defined
- sw.Do("(*in)[i].DeepCopyInto(&(*out)[i])\n", nil)
- sw.Do("}\n", nil)
- } else if uet.Kind == types.Builtin || uet.IsAssignable() {
- sw.Do("copy(*out, *in)\n", nil)
- } else {
- sw.Do("for i := range *in {\n", nil)
- if uet.Kind == types.Slice || uet.Kind == types.Map || uet.Kind == types.Pointer || deepCopyMethodOrDie(ut.Elem) != nil || deepCopyIntoMethodOrDie(ut.Elem) != nil {
- sw.Do("if (*in)[i] != nil {\n", nil)
- sw.Do("in, out := &(*in)[i], &(*out)[i]\n", nil)
- g.generateFor(ut.Elem, sw)
- sw.Do("}\n", nil)
- } else if uet.Kind == types.Interface {
- sw.Do("if (*in)[i] != nil {\n", nil)
- // Note: if t.Elem has been an alias "J" of an interface "I" in Go, we will see it
- // as kind Interface of name "J" here, i.e. generate val.DeepCopyJ(). The golang
- // parser does not give us the underlying interface name. So we cannot do any better.
- sw.Do(fmt.Sprintf("(*out)[i] = (*in)[i].DeepCopy%s()\n", uet.Name.Name), nil)
- sw.Do("}\n", nil)
- } else if uet.Kind == types.Struct {
- sw.Do("(*in)[i].DeepCopyInto(&(*out)[i])\n", nil)
- } else {
- glog.Fatalf("Hit an unsupported type %v.", uet)
- }
- sw.Do("}\n", nil)
- }
- }
- // doStruct generates code for a struct or an alias to a struct. The generated code is
- // is the same for both cases, i.e. it's the code for the underlying type.
- func (g *genDeepCopy) doStruct(t *types.Type, sw *generator.SnippetWriter) {
- ut := underlyingType(t)
- if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil {
- sw.Do("*out = in.DeepCopy()\n", nil)
- return
- }
- // Simple copy covers a lot of cases.
- sw.Do("*out = *in\n", nil)
- // Now fix-up fields as needed.
- for _, m := range ut.Members {
- ft := m.Type
- uft := underlyingType(ft)
- args := generator.Args{
- "type": ft,
- "kind": ft.Kind,
- "name": m.Name,
- }
- dc, dci := deepCopyMethodOrDie(ft), deepCopyIntoMethodOrDie(ft)
- switch {
- case dc != nil || dci != nil:
- // Note: a DeepCopyInto exists because it is added if DeepCopy is manually defined
- leftPointer := ft.Kind == types.Pointer
- rightPointer := !isReference(ft)
- if dc != nil {
- rightPointer = dc.Results[0].Kind == types.Pointer
- }
- if leftPointer == rightPointer {
- sw.Do("out.$.name$ = in.$.name$.DeepCopy()\n", args)
- } else if leftPointer {
- sw.Do("x := in.$.name$.DeepCopy()\n", args)
- sw.Do("out.$.name$ = = &x\n", args)
- } else {
- sw.Do("in.$.name$.DeepCopyInto(&out.$.name$)\n", args)
- }
- case uft.Kind == types.Builtin:
- // the initial *out = *in was enough
- case uft.Kind == types.Map, uft.Kind == types.Slice, uft.Kind == types.Pointer:
- // Fixup non-nil reference-semantic types.
- sw.Do("if in.$.name$ != nil {\n", args)
- sw.Do("in, out := &in.$.name$, &out.$.name$\n", args)
- g.generateFor(ft, sw)
- sw.Do("}\n", nil)
- case uft.Kind == types.Struct:
- if ft.IsAssignable() {
- sw.Do("out.$.name$ = in.$.name$\n", args)
- } else {
- sw.Do("in.$.name$.DeepCopyInto(&out.$.name$)\n", args)
- }
- case uft.Kind == types.Interface:
- sw.Do("if in.$.name$ != nil {\n", args)
- // Note: if t.Elem has been an alias "J" of an interface "I" in Go, we will see it
- // as kind Interface of name "J" here, i.e. generate val.DeepCopyJ(). The golang
- // parser does not give us the underlying interface name. So we cannot do any better.
- sw.Do(fmt.Sprintf("out.$.name$ = in.$.name$.DeepCopy%s()\n", uft.Name.Name), args)
- sw.Do("}\n", nil)
- default:
- glog.Fatalf("Hit an unsupported type %v.", uft)
- }
- }
- }
- // doCrossTypeStruct generates code for a struct or an alias to a struct. The generated code is
- // is the same for both cases, i.e. it's the code for the underlying type.
- func (g *genDeepCopy) doCrossTypeStruct(in *types.Type, out *types.Type, sw *generator.SnippetWriter) {
- ut := underlyingType(in)
- outUt := underlyingType(out)
- outMembers := make(map[string]types.Member, len(outUt.Members))
- for _, m := range outUt.Members {
- outMembers[m.Name] = m
- }
- if deepCopyAsMethodOrDie(ut, outUt) != nil {
- sw.Do(fmt.Sprintf("*out = in.DeepCopyAs%s()\n", outUt.Name.Name), nil)
- return
- }
- // Now fix-up fields as needed.
- for _, m := range ut.Members {
- ft := m.Type
- uft := underlyingType(ft)
- outM, ok := outMembers[m.Name]
- if !ok {
- glog.V(5).Infof("Not generating for member %v because no matched name in out struct", m)
- continue
- }
- outFt := outM.Type
- outUft := underlyingType(outFt)
- if uft.Name != outUft.Name {
- glog.V(5).Infof("Not generating for member %v because no matched type in out struct", m)
- continue
- }
- args := generator.Args{
- "type": ft,
- "kind": ft.Kind,
- "name": m.Name,
- }
- dc, dci := deepCopyMethodOrDie(ft), deepCopyIntoMethodOrDie(ft)
- switch {
- case dc != nil || dci != nil:
- // Note: a DeepCopyInto exists because it is added if DeepCopy is manually defined
- leftPointer := ft.Kind == types.Pointer
- rightPointer := !isReference(ft)
- if dc != nil {
- rightPointer = dc.Results[0].Kind == types.Pointer
- }
- if leftPointer == rightPointer {
- sw.Do("out.$.name$ = in.$.name$.DeepCopy()\n", args)
- } else if leftPointer {
- sw.Do("x := in.$.name$.DeepCopy()\n", args)
- sw.Do("out.$.name$ = = &x\n", args)
- } else {
- sw.Do("in.$.name$.DeepCopyInto(&out.$.name$)\n", args)
- }
- case uft.Kind == types.Builtin:
- sw.Do("out.$.name$ = in.$.name$\n", args)
- case uft.Kind == types.Map, uft.Kind == types.Slice, uft.Kind == types.Pointer:
- // Fixup non-nil reference-semantic types.
- sw.Do("if in.$.name$ != nil {\n", args)
- sw.Do("in, out := &in.$.name$, &out.$.name$\n", args)
- g.generateFor(ft, sw)
- sw.Do("}\n", nil)
- case uft.Kind == types.Struct:
- if ft.IsAssignable() {
- sw.Do("out.$.name$ = in.$.name$\n", args)
- } else {
- sw.Do("in.$.name$.DeepCopyInto(&out.$.name$)\n", args)
- }
- case uft.Kind == types.Interface:
- sw.Do("if in.$.name$ != nil {\n", args)
- // Note: if t.Elem has been an alias "J" of an interface "I" in Go, we will see it
- // as kind Interface of name "J" here, i.e. generate val.DeepCopyJ(). The golang
- // parser does not give us the underlying interface name. So we cannot do any better.
- sw.Do(fmt.Sprintf("out.$.name$ = in.$.name$.DeepCopy%s()\n", uft.Name.Name), args)
- sw.Do("}\n", nil)
- default:
- glog.Fatalf("Hit an unsupported type %v.", uft)
- }
- }
- }
- // doPointer generates code for a pointer or an alias to a pointer. The generated code is
- // is the same for both cases, i.e. it's the code for the underlying type.
- func (g *genDeepCopy) doPointer(t *types.Type, sw *generator.SnippetWriter) {
- ut := underlyingType(t)
- uet := underlyingType(ut.Elem)
- dc, dci := deepCopyMethodOrDie(ut.Elem), deepCopyIntoMethodOrDie(ut.Elem)
- switch {
- case dc != nil || dci != nil:
- rightPointer := !isReference(ut.Elem)
- if dc != nil {
- rightPointer = dc.Results[0].Kind == types.Pointer
- }
- if rightPointer {
- sw.Do("*out = (*in).DeepCopy()\n", nil)
- } else {
- sw.Do("x := (*in).DeepCopy()\n", nil)
- sw.Do("*out = &x\n", nil)
- }
- case uet.IsAssignable():
- sw.Do("*out = new($.Elem|raw$)\n", ut)
- sw.Do("**out = **in", nil)
- case uet.Kind == types.Map, uet.Kind == types.Slice, uet.Kind == types.Pointer:
- sw.Do("*out = new($.Elem|raw$)\n", ut)
- sw.Do("if **in != nil {\n", nil)
- sw.Do("in, out := *in, *out\n", nil)
- g.generateFor(uet, sw)
- sw.Do("}\n", nil)
- case uet.Kind == types.Struct:
- sw.Do("*out = new($.Elem|raw$)\n", ut)
- sw.Do("(*in).DeepCopyInto(*out)\n", nil)
- default:
- glog.Fatalf("Hit an unsupported type %v.", uet)
- }
- }
|