1/* 2Copyright 2015 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package generators 18 19import ( 20 "fmt" 21 "io" 22 "path/filepath" 23 "sort" 24 "strings" 25 26 "k8s.io/gengo/args" 27 "k8s.io/gengo/examples/set-gen/sets" 28 "k8s.io/gengo/generator" 29 "k8s.io/gengo/namer" 30 "k8s.io/gengo/types" 31 32 "k8s.io/klog" 33) 34 35// CustomArgs is used tby the go2idl framework to pass args specific to this 36// generator. 37type CustomArgs struct { 38 BoundingDirs []string // Only deal with types rooted under these dirs. 39} 40 41// This is the comment tag that carries parameters for deep-copy generation. 42const ( 43 tagEnabledName = "k8s:deepcopy-gen" 44 interfacesTagName = tagEnabledName + ":interfaces" 45 interfacesNonPointerTagName = tagEnabledName + ":nonpointer-interfaces" // attach the DeepCopy<Interface> methods to the 46) 47 48// Known values for the comment tag. 49const tagValuePackage = "package" 50 51// enabledTagValue holds parameters from a tagName tag. 52type enabledTagValue struct { 53 value string 54 register bool 55} 56 57func extractEnabledTypeTag(t *types.Type) *enabledTagValue { 58 comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...) 59 return extractEnabledTag(comments) 60} 61 62func extractEnabledTag(comments []string) *enabledTagValue { 63 tagVals := types.ExtractCommentTags("+", comments)[tagEnabledName] 64 if tagVals == nil { 65 // No match for the tag. 66 return nil 67 } 68 // If there are multiple values, abort. 69 if len(tagVals) > 1 { 70 klog.Fatalf("Found %d %s tags: %q", len(tagVals), tagEnabledName, tagVals) 71 } 72 73 // If we got here we are returning something. 74 tag := &enabledTagValue{} 75 76 // Get the primary value. 77 parts := strings.Split(tagVals[0], ",") 78 if len(parts) >= 1 { 79 tag.value = parts[0] 80 } 81 82 // Parse extra arguments. 83 parts = parts[1:] 84 for i := range parts { 85 kv := strings.SplitN(parts[i], "=", 2) 86 k := kv[0] 87 v := "" 88 if len(kv) == 2 { 89 v = kv[1] 90 } 91 switch k { 92 case "register": 93 if v != "false" { 94 tag.register = true 95 } 96 default: 97 klog.Fatalf("Unsupported %s param: %q", tagEnabledName, parts[i]) 98 } 99 } 100 return tag 101} 102 103// TODO: This is created only to reduce number of changes in a single PR. 104// Remove it and use PublicNamer instead. 105func deepCopyNamer() *namer.NameStrategy { 106 return &namer.NameStrategy{ 107 Join: func(pre string, in []string, post string) string { 108 return strings.Join(in, "_") 109 }, 110 PrependPackageNames: 1, 111 } 112} 113 114// NameSystems returns the name system used by the generators in this package. 115func NameSystems() namer.NameSystems { 116 return namer.NameSystems{ 117 "public": deepCopyNamer(), 118 "raw": namer.NewRawNamer("", nil), 119 } 120} 121 122// DefaultNameSystem returns the default name system for ordering the types to be 123// processed by the generators in this package. 124func DefaultNameSystem() string { 125 return "public" 126} 127 128func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages { 129 boilerplate, err := arguments.LoadGoBoilerplate() 130 if err != nil { 131 klog.Fatalf("Failed loading boilerplate: %v", err) 132 } 133 134 inputs := sets.NewString(context.Inputs...) 135 packages := generator.Packages{} 136 header := append([]byte(fmt.Sprintf("// +build !%s\n\n", arguments.GeneratedBuildTag)), boilerplate...) 137 138 boundingDirs := []string{} 139 if customArgs, ok := arguments.CustomArgs.(*CustomArgs); ok { 140 if customArgs.BoundingDirs == nil { 141 customArgs.BoundingDirs = context.Inputs 142 } 143 for i := range customArgs.BoundingDirs { 144 // Strip any trailing slashes - they are not exactly "correct" but 145 // this is friendlier. 146 boundingDirs = append(boundingDirs, strings.TrimRight(customArgs.BoundingDirs[i], "/")) 147 } 148 } 149 150 for i := range inputs { 151 klog.V(5).Infof("Considering pkg %q", i) 152 pkg := context.Universe[i] 153 if pkg == nil { 154 // If the input had no Go files, for example. 155 continue 156 } 157 158 ptag := extractEnabledTag(pkg.Comments) 159 ptagValue := "" 160 ptagRegister := false 161 if ptag != nil { 162 ptagValue = ptag.value 163 if ptagValue != tagValuePackage { 164 klog.Fatalf("Package %v: unsupported %s value: %q", i, tagEnabledName, ptagValue) 165 } 166 ptagRegister = ptag.register 167 klog.V(5).Infof(" tag.value: %q, tag.register: %t", ptagValue, ptagRegister) 168 } else { 169 klog.V(5).Infof(" no tag") 170 } 171 172 // If the pkg-scoped tag says to generate, we can skip scanning types. 173 pkgNeedsGeneration := (ptagValue == tagValuePackage) 174 if !pkgNeedsGeneration { 175 // If the pkg-scoped tag did not exist, scan all types for one that 176 // explicitly wants generation. 177 for _, t := range pkg.Types { 178 klog.V(5).Infof(" considering type %q", t.Name.String()) 179 ttag := extractEnabledTypeTag(t) 180 if ttag != nil && ttag.value == "true" { 181 klog.V(5).Infof(" tag=true") 182 if !copyableType(t) { 183 klog.Fatalf("Type %v requests deepcopy generation but is not copyable", t) 184 } 185 pkgNeedsGeneration = true 186 break 187 } 188 } 189 } 190 191 if pkgNeedsGeneration { 192 klog.V(3).Infof("Package %q needs generation", i) 193 path := pkg.Path 194 // if the source path is within a /vendor/ directory (for example, 195 // k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/apis/meta/v1), allow 196 // generation to output to the proper relative path (under vendor). 197 // Otherwise, the generator will create the file in the wrong location 198 // in the output directory. 199 // TODO: build a more fundamental concept in gengo for dealing with modifications 200 // to vendored packages. 201 if strings.HasPrefix(pkg.SourcePath, arguments.OutputBase) { 202 expandedPath := strings.TrimPrefix(pkg.SourcePath, arguments.OutputBase) 203 if strings.Contains(expandedPath, "/vendor/") { 204 path = expandedPath 205 } 206 } 207 packages = append(packages, 208 &generator.DefaultPackage{ 209 PackageName: strings.Split(filepath.Base(pkg.Path), ".")[0], 210 PackagePath: path, 211 HeaderText: header, 212 GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { 213 return []generator.Generator{ 214 NewGenDeepCopy(arguments.OutputFileBaseName, pkg.Path, boundingDirs, (ptagValue == tagValuePackage), ptagRegister), 215 } 216 }, 217 FilterFunc: func(c *generator.Context, t *types.Type) bool { 218 return t.Name.Package == pkg.Path 219 }, 220 }) 221 } 222 } 223 return packages 224} 225 226// genDeepCopy produces a file with autogenerated deep-copy functions. 227type genDeepCopy struct { 228 generator.DefaultGen 229 targetPackage string 230 boundingDirs []string 231 allTypes bool 232 registerTypes bool 233 imports namer.ImportTracker 234 typesForInit []*types.Type 235} 236 237func NewGenDeepCopy(sanitizedName, targetPackage string, boundingDirs []string, allTypes, registerTypes bool) generator.Generator { 238 return &genDeepCopy{ 239 DefaultGen: generator.DefaultGen{ 240 OptionalName: sanitizedName, 241 }, 242 targetPackage: targetPackage, 243 boundingDirs: boundingDirs, 244 allTypes: allTypes, 245 registerTypes: registerTypes, 246 imports: generator.NewImportTracker(), 247 typesForInit: make([]*types.Type, 0), 248 } 249} 250 251func (g *genDeepCopy) Namers(c *generator.Context) namer.NameSystems { 252 // Have the raw namer for this file track what it imports. 253 return namer.NameSystems{ 254 "raw": namer.NewRawNamer(g.targetPackage, g.imports), 255 } 256} 257 258func (g *genDeepCopy) Filter(c *generator.Context, t *types.Type) bool { 259 // Filter out types not being processed or not copyable within the package. 260 enabled := g.allTypes 261 if !enabled { 262 ttag := extractEnabledTypeTag(t) 263 if ttag != nil && ttag.value == "true" { 264 enabled = true 265 } 266 } 267 if !enabled { 268 return false 269 } 270 if !copyableType(t) { 271 klog.V(2).Infof("Type %v is not copyable", t) 272 return false 273 } 274 klog.V(4).Infof("Type %v is copyable", t) 275 g.typesForInit = append(g.typesForInit, t) 276 return true 277} 278 279func (g *genDeepCopy) copyableAndInBounds(t *types.Type) bool { 280 if !copyableType(t) { 281 return false 282 } 283 // Only packages within the restricted range can be processed. 284 if !isRootedUnder(t.Name.Package, g.boundingDirs) { 285 return false 286 } 287 return true 288} 289 290// deepCopyMethod returns the signature of a DeepCopy() method, nil or an error 291// if the type does not match. This allows more efficient deep copy 292// implementations to be defined by the type's author. The correct signature 293// for a type T is: 294// func (t T) DeepCopy() T 295// or: 296// func (t *T) DeepCopy() *T 297func deepCopyMethod(t *types.Type) (*types.Signature, error) { 298 f, found := t.Methods["DeepCopy"] 299 if !found { 300 return nil, nil 301 } 302 if len(f.Signature.Parameters) != 0 { 303 return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected no parameters", t) 304 } 305 if len(f.Signature.Results) != 1 { 306 return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected exactly one result", t) 307 } 308 309 ptrResult := f.Signature.Results[0].Kind == types.Pointer && f.Signature.Results[0].Elem.Name == t.Name 310 nonPtrResult := f.Signature.Results[0].Name == t.Name 311 312 if !ptrResult && !nonPtrResult { 313 return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected to return %s or *%s", t, t.Name.Name, t.Name.Name) 314 } 315 316 ptrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Kind == types.Pointer && f.Signature.Receiver.Elem.Name == t.Name 317 nonPtrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Name == t.Name 318 319 if ptrRcvr && !ptrResult { 320 return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected a *%s result for a *%s receiver", t, t.Name.Name, t.Name.Name) 321 } 322 if nonPtrRcvr && !nonPtrResult { 323 return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected a %s result for a %s receiver", t, t.Name.Name, t.Name.Name) 324 } 325 326 return f.Signature, nil 327} 328 329// deepCopyMethodOrDie returns the signatrue of a DeepCopy method, nil or calls klog.Fatalf 330// if the type does not match. 331func deepCopyMethodOrDie(t *types.Type) *types.Signature { 332 ret, err := deepCopyMethod(t) 333 if err != nil { 334 klog.Fatal(err) 335 } 336 return ret 337} 338 339// deepCopyIntoMethod returns the signature of a DeepCopyInto() method, nil or an error 340// if the type is wrong. DeepCopyInto allows more efficient deep copy 341// implementations to be defined by the type's author. The correct signature 342// for a type T is: 343// func (t T) DeepCopyInto(t *T) 344// or: 345// func (t *T) DeepCopyInto(t *T) 346func deepCopyIntoMethod(t *types.Type) (*types.Signature, error) { 347 f, found := t.Methods["DeepCopyInto"] 348 if !found { 349 return nil, nil 350 } 351 if len(f.Signature.Parameters) != 1 { 352 return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected exactly one parameter", t) 353 } 354 if len(f.Signature.Results) != 0 { 355 return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected no result type", t) 356 } 357 358 ptrParam := f.Signature.Parameters[0].Kind == types.Pointer && f.Signature.Parameters[0].Elem.Name == t.Name 359 360 if !ptrParam { 361 return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected parameter of type *%s", t, t.Name.Name) 362 } 363 364 ptrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Kind == types.Pointer && f.Signature.Receiver.Elem.Name == t.Name 365 nonPtrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Name == t.Name 366 367 if !ptrRcvr && !nonPtrRcvr { 368 // this should never happen 369 return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected a receiver of type %s or *%s", t, t.Name.Name, t.Name.Name) 370 } 371 372 return f.Signature, nil 373} 374 375// deepCopyIntoMethodOrDie returns the signature of a DeepCopyInto() method, nil or calls klog.Fatalf 376// if the type is wrong. 377func deepCopyIntoMethodOrDie(t *types.Type) *types.Signature { 378 ret, err := deepCopyIntoMethod(t) 379 if err != nil { 380 klog.Fatal(err) 381 } 382 return ret 383} 384 385func isRootedUnder(pkg string, roots []string) bool { 386 // Add trailing / to avoid false matches, e.g. foo/bar vs foo/barn. This 387 // assumes that bounding dirs do not have trailing slashes. 388 pkg = pkg + "/" 389 for _, root := range roots { 390 if strings.HasPrefix(pkg, root+"/") { 391 return true 392 } 393 } 394 return false 395} 396 397func copyableType(t *types.Type) bool { 398 // If the type opts out of copy-generation, stop. 399 ttag := extractEnabledTypeTag(t) 400 if ttag != nil && ttag.value == "false" { 401 return false 402 } 403 404 // Filter out private types. 405 if namer.IsPrivateGoName(t.Name.Name) { 406 return false 407 } 408 409 if t.Kind == types.Alias { 410 // if the underlying built-in is not deepcopy-able, deepcopy is opt-in through definition of custom methods. 411 // Note that aliases of builtins, maps, slices can have deepcopy methods. 412 if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil { 413 return true 414 } else { 415 return t.Underlying.Kind != types.Builtin || copyableType(t.Underlying) 416 } 417 } 418 419 if t.Kind != types.Struct { 420 return false 421 } 422 423 return true 424} 425 426func underlyingType(t *types.Type) *types.Type { 427 for t.Kind == types.Alias { 428 t = t.Underlying 429 } 430 return t 431} 432 433func (g *genDeepCopy) isOtherPackage(pkg string) bool { 434 if pkg == g.targetPackage { 435 return false 436 } 437 if strings.HasSuffix(pkg, "\""+g.targetPackage+"\"") { 438 return false 439 } 440 return true 441} 442 443func (g *genDeepCopy) Imports(c *generator.Context) (imports []string) { 444 importLines := []string{} 445 for _, singleImport := range g.imports.ImportLines() { 446 if g.isOtherPackage(singleImport) { 447 importLines = append(importLines, singleImport) 448 } 449 } 450 return importLines 451} 452 453func argsFromType(ts ...*types.Type) generator.Args { 454 a := generator.Args{ 455 "type": ts[0], 456 } 457 for i, t := range ts { 458 a[fmt.Sprintf("type%d", i+1)] = t 459 } 460 return a 461} 462 463func (g *genDeepCopy) Init(c *generator.Context, w io.Writer) error { 464 return nil 465} 466 467func (g *genDeepCopy) needsGeneration(t *types.Type) bool { 468 tag := extractEnabledTypeTag(t) 469 tv := "" 470 if tag != nil { 471 tv = tag.value 472 if tv != "true" && tv != "false" { 473 klog.Fatalf("Type %v: unsupported %s value: %q", t, tagEnabledName, tag.value) 474 } 475 } 476 if g.allTypes && tv == "false" { 477 // The whole package is being generated, but this type has opted out. 478 klog.V(5).Infof("Not generating for type %v because type opted out", t) 479 return false 480 } 481 if !g.allTypes && tv != "true" { 482 // The whole package is NOT being generated, and this type has NOT opted in. 483 klog.V(5).Infof("Not generating for type %v because type did not opt in", t) 484 return false 485 } 486 return true 487} 488 489func extractInterfacesTag(t *types.Type) []string { 490 var result []string 491 comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...) 492 values := types.ExtractCommentTags("+", comments)[interfacesTagName] 493 for _, v := range values { 494 if len(v) == 0 { 495 continue 496 } 497 intfs := strings.Split(v, ",") 498 for _, intf := range intfs { 499 if intf == "" { 500 continue 501 } 502 result = append(result, intf) 503 } 504 } 505 return result 506} 507 508func extractNonPointerInterfaces(t *types.Type) (bool, error) { 509 comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...) 510 values := types.ExtractCommentTags("+", comments)[interfacesNonPointerTagName] 511 if len(values) == 0 { 512 return false, nil 513 } 514 result := values[0] == "true" 515 for _, v := range values { 516 if v == "true" != result { 517 return false, fmt.Errorf("contradicting %v value %q found to previous value %v", interfacesNonPointerTagName, v, result) 518 } 519 } 520 return result, nil 521} 522 523func (g *genDeepCopy) deepCopyableInterfacesInner(c *generator.Context, t *types.Type) ([]*types.Type, error) { 524 if t.Kind != types.Struct { 525 return nil, nil 526 } 527 528 intfs := extractInterfacesTag(t) 529 530 var ts []*types.Type 531 for _, intf := range intfs { 532 t := types.ParseFullyQualifiedName(intf) 533 c.AddDir(t.Package) 534 intfT := c.Universe.Type(t) 535 if intfT == nil { 536 return nil, fmt.Errorf("unknown type %q in %s tag of type %s", intf, interfacesTagName, intfT) 537 } 538 if intfT.Kind != types.Interface { 539 return nil, fmt.Errorf("type %q in %s tag of type %s is not an interface, but: %q", intf, interfacesTagName, t, intfT.Kind) 540 } 541 g.imports.AddType(intfT) 542 ts = append(ts, intfT) 543 } 544 545 return ts, nil 546} 547 548// deepCopyableInterfaces returns the interface types to implement and whether they apply to a non-pointer receiver. 549func (g *genDeepCopy) deepCopyableInterfaces(c *generator.Context, t *types.Type) ([]*types.Type, bool, error) { 550 ts, err := g.deepCopyableInterfacesInner(c, t) 551 if err != nil { 552 return nil, false, err 553 } 554 555 set := map[string]*types.Type{} 556 for _, t := range ts { 557 set[t.String()] = t 558 } 559 560 result := []*types.Type{} 561 for _, t := range set { 562 result = append(result, t) 563 } 564 565 TypeSlice(result).Sort() // we need a stable sorting because it determines the order in generation 566 567 nonPointerReceiver, err := extractNonPointerInterfaces(t) 568 if err != nil { 569 return nil, false, err 570 } 571 572 return result, nonPointerReceiver, nil 573} 574 575type TypeSlice []*types.Type 576 577func (s TypeSlice) Len() int { return len(s) } 578func (s TypeSlice) Less(i, j int) bool { return s[i].String() < s[j].String() } 579func (s TypeSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 580func (s TypeSlice) Sort() { sort.Sort(s) } 581 582func (g *genDeepCopy) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { 583 if !g.needsGeneration(t) { 584 return nil 585 } 586 klog.V(5).Infof("Generating deepcopy function for type %v", t) 587 588 sw := generator.NewSnippetWriter(w, c, "$", "$") 589 args := argsFromType(t) 590 591 if deepCopyIntoMethodOrDie(t) == nil { 592 sw.Do("// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\n", args) 593 if isReference(t) { 594 sw.Do("func (in $.type|raw$) DeepCopyInto(out *$.type|raw$) {\n", args) 595 sw.Do("{in:=&in\n", nil) 596 } else { 597 sw.Do("func (in *$.type|raw$) DeepCopyInto(out *$.type|raw$) {\n", args) 598 } 599 if deepCopyMethodOrDie(t) != nil { 600 if t.Methods["DeepCopy"].Signature.Receiver.Kind == types.Pointer { 601 sw.Do("clone := in.DeepCopy()\n", nil) 602 sw.Do("*out = *clone\n", nil) 603 } else { 604 sw.Do("*out = in.DeepCopy()\n", nil) 605 } 606 sw.Do("return\n", nil) 607 } else { 608 g.generateFor(t, sw) 609 sw.Do("return\n", nil) 610 } 611 if isReference(t) { 612 sw.Do("}\n", nil) 613 } 614 sw.Do("}\n\n", nil) 615 } 616 617 if deepCopyMethodOrDie(t) == nil { 618 sw.Do("// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new $.type|raw$.\n", args) 619 if isReference(t) { 620 sw.Do("func (in $.type|raw$) DeepCopy() $.type|raw$ {\n", args) 621 } else { 622 sw.Do("func (in *$.type|raw$) DeepCopy() *$.type|raw$ {\n", args) 623 } 624 sw.Do("if in == nil { return nil }\n", nil) 625 sw.Do("out := new($.type|raw$)\n", args) 626 sw.Do("in.DeepCopyInto(out)\n", nil) 627 if isReference(t) { 628 sw.Do("return *out\n", nil) 629 } else { 630 sw.Do("return out\n", nil) 631 } 632 sw.Do("}\n\n", nil) 633 } 634 635 intfs, nonPointerReceiver, err := g.deepCopyableInterfaces(c, t) 636 if err != nil { 637 return err 638 } 639 for _, intf := range intfs { 640 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)) 641 if nonPointerReceiver { 642 sw.Do(fmt.Sprintf("func (in $.type|raw$) DeepCopy%s() $.type2|raw$ {\n", intf.Name.Name), argsFromType(t, intf)) 643 sw.Do("return *in.DeepCopy()", nil) 644 sw.Do("}\n\n", nil) 645 } else { 646 sw.Do(fmt.Sprintf("func (in *$.type|raw$) DeepCopy%s() $.type2|raw$ {\n", intf.Name.Name), argsFromType(t, intf)) 647 sw.Do("if c := in.DeepCopy(); c != nil {\n", nil) 648 sw.Do("return c\n", nil) 649 sw.Do("}\n", nil) 650 sw.Do("return nil\n", nil) 651 sw.Do("}\n\n", nil) 652 } 653 } 654 655 return sw.Error() 656} 657 658// isReference return true for pointer, maps, slices and aliases of those. 659func isReference(t *types.Type) bool { 660 if t.Kind == types.Pointer || t.Kind == types.Map || t.Kind == types.Slice { 661 return true 662 } 663 return t.Kind == types.Alias && isReference(underlyingType(t)) 664} 665 666// we use the system of shadowing 'in' and 'out' so that the same code is valid 667// at any nesting level. This makes the autogenerator easy to understand, and 668// the compiler shouldn't care. 669func (g *genDeepCopy) generateFor(t *types.Type, sw *generator.SnippetWriter) { 670 // derive inner types if t is an alias. We call the do* methods below with the alias type. 671 // basic rule: generate according to inner type, but construct objects with the alias type. 672 ut := underlyingType(t) 673 674 var f func(*types.Type, *generator.SnippetWriter) 675 switch ut.Kind { 676 case types.Builtin: 677 f = g.doBuiltin 678 case types.Map: 679 f = g.doMap 680 case types.Slice: 681 f = g.doSlice 682 case types.Struct: 683 f = g.doStruct 684 case types.Pointer: 685 f = g.doPointer 686 case types.Interface: 687 // interfaces are handled in-line in the other cases 688 klog.Fatalf("Hit an interface type %v. This should never happen.", t) 689 case types.Alias: 690 // can never happen because we branch on the underlying type which is never an alias 691 klog.Fatalf("Hit an alias type %v. This should never happen.", t) 692 default: 693 klog.Fatalf("Hit an unsupported type %v.", t) 694 } 695 f(t, sw) 696} 697 698// doBuiltin generates code for a builtin or an alias to a builtin. The generated code is 699// is the same for both cases, i.e. it's the code for the underlying type. 700func (g *genDeepCopy) doBuiltin(t *types.Type, sw *generator.SnippetWriter) { 701 if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil { 702 sw.Do("*out = in.DeepCopy()\n", nil) 703 return 704 } 705 706 sw.Do("*out = *in\n", nil) 707} 708 709// doMap generates code for a map or an alias to a map. The generated code is 710// is the same for both cases, i.e. it's the code for the underlying type. 711func (g *genDeepCopy) doMap(t *types.Type, sw *generator.SnippetWriter) { 712 ut := underlyingType(t) 713 uet := underlyingType(ut.Elem) 714 715 if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil { 716 sw.Do("*out = in.DeepCopy()\n", nil) 717 return 718 } 719 720 if !ut.Key.IsAssignable() { 721 klog.Fatalf("Hit an unsupported type %v.", uet) 722 } 723 724 sw.Do("*out = make($.|raw$, len(*in))\n", t) 725 sw.Do("for key, val := range *in {\n", nil) 726 dc, dci := deepCopyMethodOrDie(ut.Elem), deepCopyIntoMethodOrDie(ut.Elem) 727 switch { 728 case dc != nil || dci != nil: 729 // Note: a DeepCopy exists because it is added if DeepCopyInto is manually defined 730 leftPointer := ut.Elem.Kind == types.Pointer 731 rightPointer := !isReference(ut.Elem) 732 if dc != nil { 733 rightPointer = dc.Results[0].Kind == types.Pointer 734 } 735 if leftPointer == rightPointer { 736 sw.Do("(*out)[key] = val.DeepCopy()\n", nil) 737 } else if leftPointer { 738 sw.Do("x := val.DeepCopy()\n", nil) 739 sw.Do("(*out)[key] = &x\n", nil) 740 } else { 741 sw.Do("(*out)[key] = *val.DeepCopy()\n", nil) 742 } 743 case ut.Elem.IsAnonymousStruct(): // not uet here because it needs type cast 744 sw.Do("(*out)[key] = val\n", nil) 745 case uet.IsAssignable(): 746 sw.Do("(*out)[key] = val\n", nil) 747 case uet.Kind == types.Interface: 748 sw.Do("if val == nil {(*out)[key]=nil} else {\n", nil) 749 // Note: if t.Elem has been an alias "J" of an interface "I" in Go, we will see it 750 // as kind Interface of name "J" here, i.e. generate val.DeepCopyJ(). The golang 751 // parser does not give us the underlying interface name. So we cannot do any better. 752 sw.Do(fmt.Sprintf("(*out)[key] = val.DeepCopy%s()\n", uet.Name.Name), nil) 753 sw.Do("}\n", nil) 754 case uet.Kind == types.Slice || uet.Kind == types.Map || uet.Kind == types.Pointer: 755 sw.Do("var outVal $.|raw$\n", uet) 756 sw.Do("if val == nil { (*out)[key] = nil } else {\n", nil) 757 sw.Do("in, out := &val, &outVal\n", uet) 758 g.generateFor(ut.Elem, sw) 759 sw.Do("}\n", nil) 760 sw.Do("(*out)[key] = outVal\n", nil) 761 case uet.Kind == types.Struct: 762 sw.Do("(*out)[key] = *val.DeepCopy()\n", uet) 763 default: 764 klog.Fatalf("Hit an unsupported type %v.", uet) 765 } 766 sw.Do("}\n", nil) 767} 768 769// doSlice generates code for a slice or an alias to a slice. The generated code is 770// is the same for both cases, i.e. it's the code for the underlying type. 771func (g *genDeepCopy) doSlice(t *types.Type, sw *generator.SnippetWriter) { 772 ut := underlyingType(t) 773 uet := underlyingType(ut.Elem) 774 775 if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil { 776 sw.Do("*out = in.DeepCopy()\n", nil) 777 return 778 } 779 780 sw.Do("*out = make($.|raw$, len(*in))\n", t) 781 if deepCopyMethodOrDie(ut.Elem) != nil || deepCopyIntoMethodOrDie(ut.Elem) != nil { 782 sw.Do("for i := range *in {\n", nil) 783 // Note: a DeepCopyInto exists because it is added if DeepCopy is manually defined 784 sw.Do("(*in)[i].DeepCopyInto(&(*out)[i])\n", nil) 785 sw.Do("}\n", nil) 786 } else if uet.Kind == types.Builtin || uet.IsAssignable() { 787 sw.Do("copy(*out, *in)\n", nil) 788 } else { 789 sw.Do("for i := range *in {\n", nil) 790 if uet.Kind == types.Slice || uet.Kind == types.Map || uet.Kind == types.Pointer || deepCopyMethodOrDie(ut.Elem) != nil || deepCopyIntoMethodOrDie(ut.Elem) != nil { 791 sw.Do("if (*in)[i] != nil {\n", nil) 792 sw.Do("in, out := &(*in)[i], &(*out)[i]\n", nil) 793 g.generateFor(ut.Elem, sw) 794 sw.Do("}\n", nil) 795 } else if uet.Kind == types.Interface { 796 sw.Do("if (*in)[i] != nil {\n", nil) 797 // Note: if t.Elem has been an alias "J" of an interface "I" in Go, we will see it 798 // as kind Interface of name "J" here, i.e. generate val.DeepCopyJ(). The golang 799 // parser does not give us the underlying interface name. So we cannot do any better. 800 sw.Do(fmt.Sprintf("(*out)[i] = (*in)[i].DeepCopy%s()\n", uet.Name.Name), nil) 801 sw.Do("}\n", nil) 802 } else if uet.Kind == types.Struct { 803 sw.Do("(*in)[i].DeepCopyInto(&(*out)[i])\n", nil) 804 } else { 805 klog.Fatalf("Hit an unsupported type %v.", uet) 806 } 807 sw.Do("}\n", nil) 808 } 809} 810 811// doStruct generates code for a struct or an alias to a struct. The generated code is 812// is the same for both cases, i.e. it's the code for the underlying type. 813func (g *genDeepCopy) doStruct(t *types.Type, sw *generator.SnippetWriter) { 814 ut := underlyingType(t) 815 816 if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil { 817 sw.Do("*out = in.DeepCopy()\n", nil) 818 return 819 } 820 821 // Simple copy covers a lot of cases. 822 sw.Do("*out = *in\n", nil) 823 824 // Now fix-up fields as needed. 825 for _, m := range ut.Members { 826 ft := m.Type 827 uft := underlyingType(ft) 828 829 args := generator.Args{ 830 "type": ft, 831 "kind": ft.Kind, 832 "name": m.Name, 833 } 834 dc, dci := deepCopyMethodOrDie(ft), deepCopyIntoMethodOrDie(ft) 835 switch { 836 case dc != nil || dci != nil: 837 // Note: a DeepCopyInto exists because it is added if DeepCopy is manually defined 838 leftPointer := ft.Kind == types.Pointer 839 rightPointer := !isReference(ft) 840 if dc != nil { 841 rightPointer = dc.Results[0].Kind == types.Pointer 842 } 843 if leftPointer == rightPointer { 844 sw.Do("out.$.name$ = in.$.name$.DeepCopy()\n", args) 845 } else if leftPointer { 846 sw.Do("x := in.$.name$.DeepCopy()\n", args) 847 sw.Do("out.$.name$ = = &x\n", args) 848 } else { 849 sw.Do("in.$.name$.DeepCopyInto(&out.$.name$)\n", args) 850 } 851 case uft.Kind == types.Builtin: 852 // the initial *out = *in was enough 853 case uft.Kind == types.Map, uft.Kind == types.Slice, uft.Kind == types.Pointer: 854 // Fixup non-nil reference-semantic types. 855 sw.Do("if in.$.name$ != nil {\n", args) 856 sw.Do("in, out := &in.$.name$, &out.$.name$\n", args) 857 g.generateFor(ft, sw) 858 sw.Do("}\n", nil) 859 case uft.Kind == types.Struct: 860 if ft.IsAssignable() { 861 sw.Do("out.$.name$ = in.$.name$\n", args) 862 } else { 863 sw.Do("in.$.name$.DeepCopyInto(&out.$.name$)\n", args) 864 } 865 case uft.Kind == types.Interface: 866 sw.Do("if in.$.name$ != nil {\n", args) 867 // Note: if t.Elem has been an alias "J" of an interface "I" in Go, we will see it 868 // as kind Interface of name "J" here, i.e. generate val.DeepCopyJ(). The golang 869 // parser does not give us the underlying interface name. So we cannot do any better. 870 sw.Do(fmt.Sprintf("out.$.name$ = in.$.name$.DeepCopy%s()\n", uft.Name.Name), args) 871 sw.Do("}\n", nil) 872 default: 873 klog.Fatalf("Hit an unsupported type %v.", uft) 874 } 875 } 876} 877 878// doPointer generates code for a pointer or an alias to a pointer. The generated code is 879// is the same for both cases, i.e. it's the code for the underlying type. 880func (g *genDeepCopy) doPointer(t *types.Type, sw *generator.SnippetWriter) { 881 ut := underlyingType(t) 882 uet := underlyingType(ut.Elem) 883 884 dc, dci := deepCopyMethodOrDie(ut.Elem), deepCopyIntoMethodOrDie(ut.Elem) 885 switch { 886 case dc != nil || dci != nil: 887 rightPointer := !isReference(ut.Elem) 888 if dc != nil { 889 rightPointer = dc.Results[0].Kind == types.Pointer 890 } 891 if rightPointer { 892 sw.Do("*out = (*in).DeepCopy()\n", nil) 893 } else { 894 sw.Do("x := (*in).DeepCopy()\n", nil) 895 sw.Do("*out = &x\n", nil) 896 } 897 case uet.IsAssignable(): 898 sw.Do("*out = new($.Elem|raw$)\n", ut) 899 sw.Do("**out = **in", nil) 900 case uet.Kind == types.Map, uet.Kind == types.Slice, uet.Kind == types.Pointer: 901 sw.Do("*out = new($.Elem|raw$)\n", ut) 902 sw.Do("if **in != nil {\n", nil) 903 sw.Do("in, out := *in, *out\n", nil) 904 g.generateFor(uet, sw) 905 sw.Do("}\n", nil) 906 case uet.Kind == types.Struct: 907 sw.Do("*out = new($.Elem|raw$)\n", ut) 908 sw.Do("(*in).DeepCopyInto(*out)\n", nil) 909 default: 910 klog.Fatalf("Hit an unsupported type %v.", uet) 911 } 912} 913