mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-11 13:02:14 +00:00
Generate unsafe conversions
This commit is contained in:
parent
5b18b4e515
commit
2b1790cc5f
@ -21,6 +21,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"k8s.io/gengo/args"
|
"k8s.io/gengo/args"
|
||||||
@ -36,10 +37,11 @@ import (
|
|||||||
// generator.
|
// generator.
|
||||||
type CustomArgs struct {
|
type CustomArgs struct {
|
||||||
ExtraPeerDirs []string // Always consider these as last-ditch possibilities for conversions.
|
ExtraPeerDirs []string // Always consider these as last-ditch possibilities for conversions.
|
||||||
// SkipDefaulters indicates whether defaulter functions should be a part of conversion
|
// Skipunsafe indicates whether to generate unsafe conversions to improve the efficiency
|
||||||
// This field was introduced to ease the transition to removing defaulters from conversion.
|
// of these operations. The unsafe operation is a direct pointer assignment via unsafe
|
||||||
// It will be removed in 1.6.
|
// (within the allowed uses of unsafe) and is equivalent to a proposed Golang change to
|
||||||
SkipDefaulters bool
|
// allow structs that are identical to be assigned to each other.
|
||||||
|
SkipUnsafe bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the comment tag that carries parameters for conversion generation.
|
// This is the comment tag that carries parameters for conversion generation.
|
||||||
@ -49,6 +51,16 @@ func extractTag(comments []string) []string {
|
|||||||
return types.ExtractCommentTags("+", comments)[tagName]
|
return types.ExtractCommentTags("+", comments)[tagName]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isCopyOnly(comments []string) bool {
|
||||||
|
values := types.ExtractCommentTags("+", comments)["k8s:conversion-fn"]
|
||||||
|
return len(values) == 1 && values[0] == "copy-only"
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDrop(comments []string) bool {
|
||||||
|
values := types.ExtractCommentTags("+", comments)["k8s:conversion-fn"]
|
||||||
|
return len(values) == 1 && values[0] == "drop"
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: This is created only to reduce number of changes in a single PR.
|
// TODO: This is created only to reduce number of changes in a single PR.
|
||||||
// Remove it and use PublicNamer instead.
|
// Remove it and use PublicNamer instead.
|
||||||
func conversionNamer() *namer.NameStrategy {
|
func conversionNamer() *namer.NameStrategy {
|
||||||
@ -156,56 +168,6 @@ func getManualConversionFunctions(context *generator.Context, pkg *types.Package
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// All of the types in conversions map are of type "DeclarationOf" with
|
|
||||||
// the underlying type being "Func".
|
|
||||||
type defaulterFuncMap map[*types.Type]*types.Type
|
|
||||||
|
|
||||||
// Returns all manually-defined defaulting functions in the package.
|
|
||||||
func getManualDefaultingFunctions(context *generator.Context, pkg *types.Package, manualMap defaulterFuncMap) {
|
|
||||||
buffer := &bytes.Buffer{}
|
|
||||||
sw := generator.NewSnippetWriter(buffer, context, "$", "$")
|
|
||||||
|
|
||||||
for _, f := range pkg.Functions {
|
|
||||||
if f.Underlying == nil || f.Underlying.Kind != types.Func {
|
|
||||||
glog.Errorf("Malformed function: %#v", f)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if f.Underlying.Signature == nil {
|
|
||||||
glog.Errorf("Function without signature: %#v", f)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
signature := f.Underlying.Signature
|
|
||||||
// Check whether the function is conversion function.
|
|
||||||
// Note that all of them have signature:
|
|
||||||
// func Convert_inType_To_outType(inType, outType, conversion.Scope) error
|
|
||||||
if signature.Receiver != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(signature.Parameters) != 1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(signature.Results) != 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
inType := signature.Parameters[0]
|
|
||||||
if inType.Kind != types.Pointer {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Now check if the name satisfies the convention.
|
|
||||||
args := defaultingArgsFromType(inType.Elem)
|
|
||||||
sw.Do("$.inType|defaultfn$", args)
|
|
||||||
if f.Name.Name == buffer.String() {
|
|
||||||
key := inType.Elem
|
|
||||||
// We might scan the same package twice, and that's OK.
|
|
||||||
if v, ok := manualMap[key]; ok && v != nil && v.Name.Package != pkg.Path {
|
|
||||||
panic(fmt.Sprintf("duplicate static defaulter defined: %#v", key))
|
|
||||||
}
|
|
||||||
manualMap[key] = f
|
|
||||||
}
|
|
||||||
buffer.Reset()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
|
func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
|
||||||
boilerplate, err := arguments.LoadGoBoilerplate()
|
boilerplate, err := arguments.LoadGoBoilerplate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -217,10 +179,17 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
|
|||||||
header := append([]byte(fmt.Sprintf("// +build !%s\n\n", arguments.GeneratedBuildTag)), boilerplate...)
|
header := append([]byte(fmt.Sprintf("// +build !%s\n\n", arguments.GeneratedBuildTag)), boilerplate...)
|
||||||
header = append(header, []byte("\n// This file was autogenerated by conversion-gen. Do not edit it manually!\n\n")...)
|
header = append(header, []byte("\n// This file was autogenerated by conversion-gen. Do not edit it manually!\n\n")...)
|
||||||
|
|
||||||
// Accumulate pre-existing conversion and default functions.
|
// Accumulate pre-existing conversion functions.
|
||||||
// TODO: This is too ad-hoc. We need a better way.
|
// TODO: This is too ad-hoc. We need a better way.
|
||||||
manualConversions := conversionFuncMap{}
|
manualConversions := conversionFuncMap{}
|
||||||
manualDefaults := defaulterFuncMap{}
|
|
||||||
|
// Record types that are memory equivalent. A type is memory equivalent
|
||||||
|
// if it has the same memory layout and no nested manual conversion is
|
||||||
|
// defined.
|
||||||
|
// TODO: in the future, relax the nested manual conversion requirement
|
||||||
|
// if we can show that a large enough types are memory identical but
|
||||||
|
// have non-trivial conversion
|
||||||
|
memoryEquivalentTypes := equalMemoryTypes{}
|
||||||
|
|
||||||
// We are generating conversions only for packages that are explicitly
|
// We are generating conversions only for packages that are explicitly
|
||||||
// passed as InputDir.
|
// passed as InputDir.
|
||||||
@ -234,7 +203,6 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
|
|||||||
|
|
||||||
// Add conversion and defaulting functions.
|
// Add conversion and defaulting functions.
|
||||||
getManualConversionFunctions(context, pkg, manualConversions)
|
getManualConversionFunctions(context, pkg, manualConversions)
|
||||||
getManualDefaultingFunctions(context, pkg, manualDefaults)
|
|
||||||
|
|
||||||
// Only generate conversions for packages which explicitly request it
|
// Only generate conversions for packages which explicitly request it
|
||||||
// by specifying one or more "+k8s:conversion-gen=<peer-pkg>"
|
// by specifying one or more "+k8s:conversion-gen=<peer-pkg>"
|
||||||
@ -246,18 +214,23 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
|
|||||||
glog.V(5).Infof(" no tag")
|
glog.V(5).Infof(" no tag")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
skipDefaulters := false
|
skipUnsafe := false
|
||||||
if customArgs, ok := arguments.CustomArgs.(*CustomArgs); ok {
|
if customArgs, ok := arguments.CustomArgs.(*CustomArgs); ok {
|
||||||
if len(customArgs.ExtraPeerDirs) > 0 {
|
if len(customArgs.ExtraPeerDirs) > 0 {
|
||||||
peerPkgs = append(peerPkgs, customArgs.ExtraPeerDirs...)
|
peerPkgs = append(peerPkgs, customArgs.ExtraPeerDirs...)
|
||||||
}
|
}
|
||||||
skipDefaulters = customArgs.SkipDefaulters
|
skipUnsafe = customArgs.SkipUnsafe
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure our peer-packages are added and fully parsed.
|
// Make sure our peer-packages are added and fully parsed.
|
||||||
for _, pp := range peerPkgs {
|
for _, pp := range peerPkgs {
|
||||||
context.AddDir(pp)
|
context.AddDir(pp)
|
||||||
getManualConversionFunctions(context, context.Universe[pp], manualConversions)
|
getManualConversionFunctions(context, context.Universe[pp], manualConversions)
|
||||||
getManualDefaultingFunctions(context, context.Universe[pp], manualDefaults)
|
}
|
||||||
|
|
||||||
|
unsafeEquality := TypesEqual(memoryEquivalentTypes)
|
||||||
|
if skipUnsafe {
|
||||||
|
unsafeEquality = noEquality{}
|
||||||
}
|
}
|
||||||
|
|
||||||
packages = append(packages,
|
packages = append(packages,
|
||||||
@ -267,7 +240,7 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
|
|||||||
HeaderText: header,
|
HeaderText: header,
|
||||||
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
|
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
|
||||||
return []generator.Generator{
|
return []generator.Generator{
|
||||||
NewGenConversion(arguments.OutputFileBaseName, pkg.Path, manualConversions, manualDefaults, peerPkgs, !skipDefaulters),
|
NewGenConversion(arguments.OutputFileBaseName, pkg.Path, manualConversions, peerPkgs, unsafeEquality),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
FilterFunc: func(c *generator.Context, t *types.Type) bool {
|
FilterFunc: func(c *generator.Context, t *types.Type) bool {
|
||||||
@ -275,9 +248,79 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there is a manual conversion defined between two types, exclude it
|
||||||
|
// from being a candidate for unsafe conversion
|
||||||
|
for k, v := range manualConversions {
|
||||||
|
if isCopyOnly(v.CommentLines) {
|
||||||
|
glog.V(5).Infof("Conversion function %s will not block memory copy because it is copy-only", v.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// this type should be excluded from all equivalence, because the converter must be called.
|
||||||
|
memoryEquivalentTypes.Skip(k.inType, k.outType)
|
||||||
|
}
|
||||||
|
|
||||||
return packages
|
return packages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type equalMemoryTypes map[conversionPair]bool
|
||||||
|
|
||||||
|
func (e equalMemoryTypes) Skip(a, b *types.Type) {
|
||||||
|
e[conversionPair{a, b}] = false
|
||||||
|
e[conversionPair{b, a}] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e equalMemoryTypes) Equal(a, b *types.Type) bool {
|
||||||
|
if a == b {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if equal, ok := e[conversionPair{a, b}]; ok {
|
||||||
|
return equal
|
||||||
|
}
|
||||||
|
if equal, ok := e[conversionPair{b, a}]; ok {
|
||||||
|
return equal
|
||||||
|
}
|
||||||
|
result := e.equal(a, b)
|
||||||
|
e[conversionPair{a, b}] = result
|
||||||
|
e[conversionPair{b, a}] = result
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e equalMemoryTypes) equal(a, b *types.Type) bool {
|
||||||
|
in, out := unwrapAlias(a), unwrapAlias(b)
|
||||||
|
switch {
|
||||||
|
case in == out:
|
||||||
|
return true
|
||||||
|
case in.Kind == out.Kind:
|
||||||
|
switch in.Kind {
|
||||||
|
case types.Struct:
|
||||||
|
if len(in.Members) != len(out.Members) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, inMember := range in.Members {
|
||||||
|
outMember := out.Members[i]
|
||||||
|
if !e.Equal(inMember.Type, outMember.Type) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case types.Pointer:
|
||||||
|
return e.Equal(in.Elem, out.Elem)
|
||||||
|
case types.Map:
|
||||||
|
return e.Equal(in.Key, out.Key) && e.Equal(in.Elem, out.Elem)
|
||||||
|
case types.Slice:
|
||||||
|
return e.Equal(in.Elem, out.Elem)
|
||||||
|
case types.Interface:
|
||||||
|
// TODO: determine whether the interfaces are actually equivalent - for now, they must have the
|
||||||
|
// same type.
|
||||||
|
return false
|
||||||
|
case types.Builtin:
|
||||||
|
return in.Name.Name == out.Name.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func findMember(t *types.Type, name string) (types.Member, bool) {
|
func findMember(t *types.Type, name string) (types.Member, bool) {
|
||||||
if t.Kind != types.Struct {
|
if t.Kind != types.Struct {
|
||||||
return types.Member{}, false
|
return types.Member{}, false
|
||||||
@ -303,20 +346,27 @@ const (
|
|||||||
conversionPackagePath = "k8s.io/kubernetes/pkg/conversion"
|
conversionPackagePath = "k8s.io/kubernetes/pkg/conversion"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type noEquality struct{}
|
||||||
|
|
||||||
|
func (noEquality) Equal(_, _ *types.Type) bool { return false }
|
||||||
|
|
||||||
|
type TypesEqual interface {
|
||||||
|
Equal(a, b *types.Type) bool
|
||||||
|
}
|
||||||
|
|
||||||
// genConversion produces a file with a autogenerated conversions.
|
// genConversion produces a file with a autogenerated conversions.
|
||||||
type genConversion struct {
|
type genConversion struct {
|
||||||
generator.DefaultGen
|
generator.DefaultGen
|
||||||
targetPackage string
|
targetPackage string
|
||||||
peerPackages []string
|
peerPackages []string
|
||||||
manualConversions conversionFuncMap
|
manualConversions conversionFuncMap
|
||||||
manualDefaulters defaulterFuncMap
|
|
||||||
imports namer.ImportTracker
|
imports namer.ImportTracker
|
||||||
types []*types.Type
|
types []*types.Type
|
||||||
skippedFields map[*types.Type][]string
|
skippedFields map[*types.Type][]string
|
||||||
includeDefaulters bool
|
useUnsafe TypesEqual
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGenConversion(sanitizedName, targetPackage string, manualConversions conversionFuncMap, manualDefaulters defaulterFuncMap, peerPkgs []string, includeDefaulters bool) generator.Generator {
|
func NewGenConversion(sanitizedName, targetPackage string, manualConversions conversionFuncMap, peerPkgs []string, useUnsafe TypesEqual) generator.Generator {
|
||||||
return &genConversion{
|
return &genConversion{
|
||||||
DefaultGen: generator.DefaultGen{
|
DefaultGen: generator.DefaultGen{
|
||||||
OptionalName: sanitizedName,
|
OptionalName: sanitizedName,
|
||||||
@ -324,11 +374,10 @@ func NewGenConversion(sanitizedName, targetPackage string, manualConversions con
|
|||||||
targetPackage: targetPackage,
|
targetPackage: targetPackage,
|
||||||
peerPackages: peerPkgs,
|
peerPackages: peerPkgs,
|
||||||
manualConversions: manualConversions,
|
manualConversions: manualConversions,
|
||||||
manualDefaulters: manualDefaulters,
|
|
||||||
imports: generator.NewImportTracker(),
|
imports: generator.NewImportTracker(),
|
||||||
types: []*types.Type{},
|
types: []*types.Type{},
|
||||||
skippedFields: map[*types.Type][]string{},
|
skippedFields: map[*types.Type][]string{},
|
||||||
includeDefaulters: includeDefaulters,
|
useUnsafe: useUnsafe,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,6 +488,22 @@ func (g *genConversion) preexists(inType, outType *types.Type) (*types.Type, boo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *genConversion) Init(c *generator.Context, w io.Writer) error {
|
func (g *genConversion) Init(c *generator.Context, w io.Writer) error {
|
||||||
|
if glog.V(5) {
|
||||||
|
if m, ok := g.useUnsafe.(equalMemoryTypes); ok {
|
||||||
|
var result []string
|
||||||
|
glog.Infof("All objects without identical memory layout:")
|
||||||
|
for k, v := range m {
|
||||||
|
if v {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, fmt.Sprintf(" %s -> %s = %t", k.inType, k.outType, v))
|
||||||
|
}
|
||||||
|
sort.Strings(result)
|
||||||
|
for _, s := range result {
|
||||||
|
glog.Infof(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
sw := generator.NewSnippetWriter(w, c, "$", "$")
|
sw := generator.NewSnippetWriter(w, c, "$", "$")
|
||||||
sw.Do("func init() {\n", nil)
|
sw.Do("func init() {\n", nil)
|
||||||
sw.Do("SchemeBuilder.Register(RegisterConversions)\n", nil)
|
sw.Do("SchemeBuilder.Register(RegisterConversions)\n", nil)
|
||||||
@ -477,10 +542,6 @@ func (g *genConversion) generateConversion(inType, outType *types.Type, sw *gene
|
|||||||
With("Scope", types.Ref(conversionPackagePath, "Scope"))
|
With("Scope", types.Ref(conversionPackagePath, "Scope"))
|
||||||
|
|
||||||
sw.Do("func auto"+nameTmpl+"(in *$.inType|raw$, out *$.outType|raw$, s $.Scope|raw$) error {\n", args)
|
sw.Do("func auto"+nameTmpl+"(in *$.inType|raw$, out *$.outType|raw$, s $.Scope|raw$) error {\n", args)
|
||||||
// if no defaulter of form SetDefaults_XXX is defined, do not inline a check for defaulting.
|
|
||||||
if function, ok := g.manualDefaulters[inType]; ok && g.includeDefaulters {
|
|
||||||
sw.Do("$.|raw$(in)\n", function)
|
|
||||||
}
|
|
||||||
g.generateFor(inType, outType, sw)
|
g.generateFor(inType, outType, sw)
|
||||||
sw.Do("return nil\n", nil)
|
sw.Do("return nil\n", nil)
|
||||||
sw.Do("}\n\n", nil)
|
sw.Do("}\n\n", nil)
|
||||||
@ -508,6 +569,7 @@ func (g *genConversion) generateConversion(inType, outType *types.Type, sw *gene
|
|||||||
func (g *genConversion) generateFor(inType, outType *types.Type, sw *generator.SnippetWriter) {
|
func (g *genConversion) generateFor(inType, outType *types.Type, sw *generator.SnippetWriter) {
|
||||||
glog.V(5).Infof("generating %v -> %v", inType, outType)
|
glog.V(5).Infof("generating %v -> %v", inType, outType)
|
||||||
var f func(*types.Type, *types.Type, *generator.SnippetWriter)
|
var f func(*types.Type, *types.Type, *generator.SnippetWriter)
|
||||||
|
|
||||||
switch inType.Kind {
|
switch inType.Kind {
|
||||||
case types.Builtin:
|
case types.Builtin:
|
||||||
f = g.doBuiltin
|
f = g.doBuiltin
|
||||||
@ -524,6 +586,7 @@ func (g *genConversion) generateFor(inType, outType *types.Type, sw *generator.S
|
|||||||
default:
|
default:
|
||||||
f = g.doUnknown
|
f = g.doUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
f(inType, outType, sw)
|
f(inType, outType, sw)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -638,19 +701,52 @@ func (g *genConversion) doStruct(inType, outType *types.Type, sw *generator.Snip
|
|||||||
outMemberType = &copied
|
outMemberType = &copied
|
||||||
}
|
}
|
||||||
|
|
||||||
args := map[string]interface{}{
|
args := argsFromType(inMemberType, outMemberType).With("name", inMember.Name)
|
||||||
"inType": inMemberType,
|
|
||||||
"outType": outMemberType,
|
// try a direct memory copy for any type that has exactly equivalent values
|
||||||
"name": inMember.Name,
|
if g.useUnsafe.Equal(inMemberType, outMemberType) {
|
||||||
|
args = args.
|
||||||
|
With("Pointer", types.Ref("unsafe", "Pointer")).
|
||||||
|
With("SliceHeader", types.Ref("reflect", "SliceHeader"))
|
||||||
|
switch inMemberType.Kind {
|
||||||
|
case types.Pointer:
|
||||||
|
sw.Do("out.$.name$ = ($.outType|raw$)($.Pointer|raw$(in.$.name$))\n", args)
|
||||||
|
continue
|
||||||
|
case types.Map:
|
||||||
|
sw.Do("{\n", nil)
|
||||||
|
sw.Do("m := (*$.outType|raw$)($.Pointer|raw$(&in.$.name$))\n", args)
|
||||||
|
sw.Do("out.$.name$ = *m\n", args)
|
||||||
|
sw.Do("}\n", nil)
|
||||||
|
continue
|
||||||
|
case types.Slice:
|
||||||
|
sw.Do("{\n", nil)
|
||||||
|
sw.Do("outHdr := (*$.SliceHeader|raw$)($.Pointer|raw$(&out.$.name$))\n", args)
|
||||||
|
sw.Do("inHdr := (*$.SliceHeader|raw$)($.Pointer|raw$(&in.$.name$))\n", args)
|
||||||
|
sw.Do("*outHdr = *inHdr\n", nil)
|
||||||
|
sw.Do("}\n", nil)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check based on the top level name, not the underlying names
|
// check based on the top level name, not the underlying names
|
||||||
if function, ok := g.preexists(inMember.Type, outMember.Type); ok {
|
if function, ok := g.preexists(inMember.Type, outMember.Type); ok {
|
||||||
|
if isDrop(function.CommentLines) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// copy-only functions that are directly assignable can be inlined instead of invoked.
|
||||||
|
// As an example, conversion functions exist that allow types with private fields to be
|
||||||
|
// correctly copied between types. These functions are equivalent to a memory assignment,
|
||||||
|
// and are necessary for the reflection path, but should not block memory conversion.
|
||||||
|
// Convert_unversioned_Time_to_unversioned_Time is an example of this logic.
|
||||||
|
if !isCopyOnly(function.CommentLines) || !g.isFastConversion(inMemberType, outMemberType) {
|
||||||
args["function"] = function
|
args["function"] = function
|
||||||
sw.Do("if err := $.function|raw$(&in.$.name$, &out.$.name$, s); err != nil {\n", args)
|
sw.Do("if err := $.function|raw$(&in.$.name$, &out.$.name$, s); err != nil {\n", args)
|
||||||
sw.Do("return err\n", nil)
|
sw.Do("return err\n", nil)
|
||||||
sw.Do("}\n", nil)
|
sw.Do("}\n", nil)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
glog.V(5).Infof("Skipped function %s because it is copy-only and we can use direct assignment", function.Name)
|
||||||
|
}
|
||||||
|
|
||||||
// If we can't auto-convert, punt before we emit any code.
|
// If we can't auto-convert, punt before we emit any code.
|
||||||
if inMemberType.Kind != outMemberType.Kind {
|
if inMemberType.Kind != outMemberType.Kind {
|
||||||
@ -722,6 +818,17 @@ func (g *genConversion) doStruct(inType, outType *types.Type, sw *generator.Snip
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *genConversion) isFastConversion(inType, outType *types.Type) bool {
|
||||||
|
switch inType.Kind {
|
||||||
|
case types.Builtin:
|
||||||
|
return true
|
||||||
|
case types.Map, types.Slice, types.Pointer, types.Struct, types.Alias:
|
||||||
|
return g.isDirectlyAssignable(inType, outType)
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (g *genConversion) isDirectlyAssignable(inType, outType *types.Type) bool {
|
func (g *genConversion) isDirectlyAssignable(inType, outType *types.Type) bool {
|
||||||
return unwrapAlias(inType) == unwrapAlias(outType)
|
return unwrapAlias(inType) == unwrapAlias(outType)
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,6 @@ func main() {
|
|||||||
"k8s.io/kubernetes/pkg/conversion",
|
"k8s.io/kubernetes/pkg/conversion",
|
||||||
"k8s.io/kubernetes/pkg/runtime",
|
"k8s.io/kubernetes/pkg/runtime",
|
||||||
},
|
},
|
||||||
SkipDefaulters: true,
|
|
||||||
}
|
}
|
||||||
pflag.CommandLine.StringSliceVar(&customArgs.ExtraPeerDirs, "extra-peer-dirs", customArgs.ExtraPeerDirs,
|
pflag.CommandLine.StringSliceVar(&customArgs.ExtraPeerDirs, "extra-peer-dirs", customArgs.ExtraPeerDirs,
|
||||||
"Comma-separated list of import paths which are considered, after tag-specified peers, for conversions.")
|
"Comma-separated list of import paths which are considered, after tag-specified peers, for conversions.")
|
||||||
|
Loading…
Reference in New Issue
Block a user