Turn first deepcopy parameter into a pointer

pull/6/head
Dr. Stefan Schimanski 2016-07-08 12:27:38 +02:00
parent dbab1a8477
commit 6049623a13
2 changed files with 47 additions and 38 deletions

View File

@ -425,7 +425,7 @@ func (g *genDeepCopy) GenerateType(c *generator.Context, t *types.Type, w io.Wri
sw := generator.NewSnippetWriter(w, c, "$", "$") sw := generator.NewSnippetWriter(w, c, "$", "$")
funcName := g.funcNameTmpl(t) funcName := g.funcNameTmpl(t)
sw.Do(fmt.Sprintf("func %s(in $.type|raw$, out *$.type|raw$, c *$.Cloner|raw$) error {\n", funcName), g.withGlobals(argsFromType(t))) sw.Do(fmt.Sprintf("func %s(in *$.type|raw$, out *$.type|raw$, c *$.Cloner|raw$) error {\n", funcName), g.withGlobals(argsFromType(t)))
g.generateFor(t, sw) g.generateFor(t, sw)
sw.Do("return nil\n", nil) sw.Do("return nil\n", nil)
sw.Do("}\n\n", nil) sw.Do("}\n\n", nil)
@ -459,7 +459,7 @@ func (g *genDeepCopy) generateFor(t *types.Type, sw *generator.SnippetWriter) {
} }
func (g *genDeepCopy) doBuiltin(t *types.Type, sw *generator.SnippetWriter) { func (g *genDeepCopy) doBuiltin(t *types.Type, sw *generator.SnippetWriter) {
sw.Do("*out = in\n", nil) sw.Do("*out = *in\n", nil)
} }
func (g *genDeepCopy) doMap(t *types.Type, sw *generator.SnippetWriter) { func (g *genDeepCopy) doMap(t *types.Type, sw *generator.SnippetWriter) {
@ -467,63 +467,63 @@ func (g *genDeepCopy) doMap(t *types.Type, sw *generator.SnippetWriter) {
if t.Key.IsAssignable() { if t.Key.IsAssignable() {
switch { switch {
case hasDeepCopyMethod(t.Elem): case hasDeepCopyMethod(t.Elem):
sw.Do("for key, val := range in {\n", nil) sw.Do("for key, val := range *in {\n", nil)
sw.Do("(*out)[key] = val.DeepCopy()\n", nil) sw.Do("(*out)[key] = val.DeepCopy()\n", nil)
sw.Do("}\n", nil) sw.Do("}\n", nil)
case t.Elem.IsAnonymousStruct(): case t.Elem.IsAnonymousStruct():
sw.Do("for key := range in {\n", nil) sw.Do("for key := range *in {\n", nil)
sw.Do("(*out)[key] = struct{}{}\n", nil) sw.Do("(*out)[key] = struct{}{}\n", nil)
sw.Do("}\n", nil) sw.Do("}\n", nil)
case t.Elem.IsAssignable(): case t.Elem.IsAssignable():
sw.Do("for key, val := range in {\n", nil) sw.Do("for key, val := range *in {\n", nil)
sw.Do("(*out)[key] = val\n", nil) sw.Do("(*out)[key] = val\n", nil)
sw.Do("}\n", nil) sw.Do("}\n", nil)
default: default:
sw.Do("for key, val := range in {\n", nil) sw.Do("for key, val := range *in {\n", nil)
if g.copyableAndInBounds(t.Elem) { if g.copyableAndInBounds(t.Elem) {
sw.Do("newVal := new($.|raw$)\n", t.Elem) sw.Do("newVal := new($.|raw$)\n", t.Elem)
funcName := g.funcNameTmpl(t.Elem) funcName := g.funcNameTmpl(t.Elem)
sw.Do(fmt.Sprintf("if err := %s(val, newVal, c); err != nil {\n", funcName), argsFromType(t.Elem)) sw.Do(fmt.Sprintf("if err := %s(&val, newVal, c); err != nil {\n", funcName), argsFromType(t.Elem))
sw.Do("return err\n", nil) sw.Do("return err\n", nil)
sw.Do("}\n", nil) sw.Do("}\n", nil)
sw.Do("(*out)[key] = *newVal\n", nil) sw.Do("(*out)[key] = *newVal\n", nil)
} else { } else {
sw.Do("if newVal, err := c.DeepCopy(val); err != nil {\n", nil) sw.Do("if newVal, err := c.DeepCopy(&val); err != nil {\n", nil)
sw.Do("return err\n", nil) sw.Do("return err\n", nil)
sw.Do("} else {\n", nil) sw.Do("} else {\n", nil)
sw.Do("(*out)[key] = newVal.($.|raw$)\n", t.Elem) sw.Do("(*out)[key] = *newVal.(*$.|raw$)\n", t.Elem)
sw.Do("}\n", nil) sw.Do("}\n", nil)
} }
sw.Do("}\n", nil) sw.Do("}\n", nil)
} }
} else { } else {
// TODO: Implement it when necessary. // TODO: Implement it when necessary.
sw.Do("for range in {\n", nil) sw.Do("for range *in {\n", nil)
sw.Do("// FIXME: Copying unassignable keys unsupported $.|raw$\n", t.Key) sw.Do("// FIXME: Copying unassignable keys unsupported $.|raw$\n", t.Key)
sw.Do("}\n", nil) sw.Do("}\n", nil)
} }
} }
func (g *genDeepCopy) doSlice(t *types.Type, sw *generator.SnippetWriter) { func (g *genDeepCopy) doSlice(t *types.Type, sw *generator.SnippetWriter) {
sw.Do("*out = make($.|raw$, len(in))\n", t) sw.Do("*out = make($.|raw$, len(*in))\n", t)
if t.Elem.Kind == types.Builtin { if t.Elem.Kind == types.Builtin {
sw.Do("copy(*out, in)\n", nil) sw.Do("copy(*out, *in)\n", nil)
} else { } else {
sw.Do("for i := range in {\n", nil) sw.Do("for i := range *in {\n", nil)
if hasDeepCopyMethod(t.Elem) { if hasDeepCopyMethod(t.Elem) {
sw.Do("(*out)[i] = in[i].DeepCopy()\n", nil) sw.Do("(*out)[i] = (*in)[i].DeepCopy()\n", nil)
} else if t.Elem.IsAssignable() { } else if t.Elem.IsAssignable() {
sw.Do("(*out)[i] = in[i]\n", nil) sw.Do("(*out)[i] = (*in)[i]\n", nil)
} else if g.copyableAndInBounds(t.Elem) { } else if g.copyableAndInBounds(t.Elem) {
funcName := g.funcNameTmpl(t.Elem) funcName := g.funcNameTmpl(t.Elem)
sw.Do(fmt.Sprintf("if err := %s(in[i], &(*out)[i], c); err != nil {\n", funcName), argsFromType(t.Elem)) sw.Do(fmt.Sprintf("if err := %s(&(*in)[i], &(*out)[i], c); err != nil {\n", funcName), argsFromType(t.Elem))
sw.Do("return err\n", nil) sw.Do("return err\n", nil)
sw.Do("}\n", nil) sw.Do("}\n", nil)
} else { } else {
sw.Do("if newVal, err := c.DeepCopy(in[i]); err != nil {\n", nil) sw.Do("if newVal, err := c.DeepCopy(&(*in)[i]); err != nil {\n", nil)
sw.Do("return err\n", nil) sw.Do("return err\n", nil)
sw.Do("} else {\n", nil) sw.Do("} else {\n", nil)
sw.Do("(*out)[i] = newVal.($.|raw$)\n", t.Elem) sw.Do("(*out)[i] = *newVal.(*$.|raw$)\n", t.Elem)
sw.Do("}\n", nil) sw.Do("}\n", nil)
} }
sw.Do("}\n", nil) sw.Do("}\n", nil)
@ -547,7 +547,7 @@ func (g *genDeepCopy) doStruct(t *types.Type, sw *generator.SnippetWriter) {
sw.Do("out.$.name$ = in.$.name$\n", args) sw.Do("out.$.name$ = in.$.name$\n", args)
case types.Map, types.Slice, types.Pointer: case types.Map, types.Slice, types.Pointer:
sw.Do("if in.$.name$ != nil {\n", args) sw.Do("if in.$.name$ != nil {\n", args)
sw.Do("in, out := in.$.name$, &out.$.name$\n", args) sw.Do("in, out := &in.$.name$, &out.$.name$\n", args)
g.generateFor(t, sw) g.generateFor(t, sw)
sw.Do("} else {\n", nil) sw.Do("} else {\n", nil)
sw.Do("out.$.name$ = nil\n", args) sw.Do("out.$.name$ = nil\n", args)
@ -559,23 +559,23 @@ func (g *genDeepCopy) doStruct(t *types.Type, sw *generator.SnippetWriter) {
sw.Do("out.$.name$ = in.$.name$\n", args) sw.Do("out.$.name$ = in.$.name$\n", args)
} else if g.copyableAndInBounds(t) { } else if g.copyableAndInBounds(t) {
funcName := g.funcNameTmpl(t) funcName := g.funcNameTmpl(t)
sw.Do(fmt.Sprintf("if err := %s(in.$.name$, &out.$.name$, c); err != nil {\n", funcName), args) sw.Do(fmt.Sprintf("if err := %s(&in.$.name$, &out.$.name$, c); err != nil {\n", funcName), args)
sw.Do("return err\n", nil) sw.Do("return err\n", nil)
sw.Do("}\n", nil) sw.Do("}\n", nil)
} else { } else {
sw.Do("if newVal, err := c.DeepCopy(in.$.name$); err != nil {\n", args) sw.Do("if newVal, err := c.DeepCopy(&in.$.name$); err != nil {\n", args)
sw.Do("return err\n", nil) sw.Do("return err\n", nil)
sw.Do("} else {\n", nil) sw.Do("} else {\n", nil)
sw.Do("out.$.name$ = newVal.($.type|raw$)\n", args) sw.Do("out.$.name$ = *newVal.(*$.type|raw$)\n", args)
sw.Do("}\n", nil) sw.Do("}\n", nil)
} }
default: default:
sw.Do("if in.$.name$ == nil {\n", args) sw.Do("if in.$.name$ == nil {\n", args)
sw.Do("out.$.name$ = nil\n", args) sw.Do("out.$.name$ = nil\n", args)
sw.Do("} else if newVal, err := c.DeepCopy(in.$.name$); err != nil {\n", args) sw.Do("} else if newVal, err := c.DeepCopy(&in.$.name$); err != nil {\n", args)
sw.Do("return err\n", nil) sw.Do("return err\n", nil)
sw.Do("} else {\n", nil) sw.Do("} else {\n", nil)
sw.Do("out.$.name$ = newVal.($.type|raw$)\n", args) sw.Do("out.$.name$ = *newVal.(*$.type|raw$)\n", args)
sw.Do("}\n", nil) sw.Do("}\n", nil)
} }
} }
@ -587,13 +587,15 @@ func (g *genDeepCopy) doInterface(t *types.Type, sw *generator.SnippetWriter) {
} }
func (g *genDeepCopy) doPointer(t *types.Type, sw *generator.SnippetWriter) { func (g *genDeepCopy) doPointer(t *types.Type, sw *generator.SnippetWriter) {
sw.Do("*out = new($.Elem|raw$)\n", t)
if hasDeepCopyMethod(t.Elem) { if hasDeepCopyMethod(t.Elem) {
sw.Do("**out = in.DeepCopy()\n", nil) sw.Do("*out = new($.Elem|raw$)\n", t)
sw.Do("**out = (*in).DeepCopy()\n", nil)
} else if t.Elem.IsAssignable() { } else if t.Elem.IsAssignable() {
sw.Do("**out = *in", nil) sw.Do("*out = new($.Elem|raw$)\n", t)
sw.Do("**out = **in", nil)
} else if g.copyableAndInBounds(t.Elem) { } else if g.copyableAndInBounds(t.Elem) {
funcName := g.funcNameTmpl(t.Elem) funcName := g.funcNameTmpl(t.Elem)
sw.Do("*out = new($.Elem|raw$)\n", t)
sw.Do(fmt.Sprintf("if err := %s(*in, *out, c); err != nil {\n", funcName), argsFromType(t.Elem)) sw.Do(fmt.Sprintf("if err := %s(*in, *out, c); err != nil {\n", funcName), argsFromType(t.Elem))
sw.Do("return err\n", nil) sw.Do("return err\n", nil)
sw.Do("}\n", nil) sw.Do("}\n", nil)
@ -601,7 +603,7 @@ func (g *genDeepCopy) doPointer(t *types.Type, sw *generator.SnippetWriter) {
sw.Do("if newVal, err := c.DeepCopy(*in); err != nil {\n", nil) sw.Do("if newVal, err := c.DeepCopy(*in); err != nil {\n", nil)
sw.Do("return err\n", nil) sw.Do("return err\n", nil)
sw.Do("} else {\n", nil) sw.Do("} else {\n", nil)
sw.Do("**out = newVal.($.|raw$)\n", t.Elem) sw.Do("*out = newVal.($.|raw$)\n", t.Elem)
sw.Do("}\n", nil) sw.Do("}\n", nil)
} }
} }

View File

@ -42,10 +42,10 @@ func NewCloner() *Cloner {
} }
// Prevent recursing into every byte... // Prevent recursing into every byte...
func byteSliceDeepCopy(in []byte, out *[]byte, c *Cloner) error { func byteSliceDeepCopy(in *[]byte, out *[]byte, c *Cloner) error {
if in != nil { if *in != nil {
*out = make([]byte, len(in)) *out = make([]byte, len(*in))
copy(*out, in) copy(*out, *in)
} else { } else {
*out = nil *out = nil
} }
@ -63,10 +63,10 @@ func verifyDeepCopyFunctionSignature(ft reflect.Type) error {
if ft.NumOut() != 1 { if ft.NumOut() != 1 {
return fmt.Errorf("expected one 'out' param, got %v", ft) return fmt.Errorf("expected one 'out' param, got %v", ft)
} }
if ft.In(1).Kind() != reflect.Ptr { if ft.In(0).Kind() != reflect.Ptr {
return fmt.Errorf("expected pointer arg for 'in' param 1, got: %v", ft) return fmt.Errorf("expected pointer arg for 'in' param 0, got: %v", ft)
} }
if ft.In(1).Elem() != ft.In(0) { if ft.In(1) != ft.In(0) {
return fmt.Errorf("expected 'in' param 0 the same as param 1, got: %v", ft) return fmt.Errorf("expected 'in' param 0 the same as param 1, got: %v", ft)
} }
var forClonerType Cloner var forClonerType Cloner
@ -135,6 +135,13 @@ func (c *Cloner) DeepCopy(in interface{}) (interface{}, error) {
func (c *Cloner) deepCopy(src reflect.Value) (reflect.Value, error) { func (c *Cloner) deepCopy(src reflect.Value) (reflect.Value, error) {
inType := src.Type() inType := src.Type()
switch src.Kind() {
case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
if src.IsNil() {
return src, nil
}
}
if fv, ok := c.deepCopyFuncs[inType]; ok { if fv, ok := c.deepCopyFuncs[inType]; ok {
return c.customDeepCopy(src, fv) return c.customDeepCopy(src, fv)
} }
@ -145,15 +152,15 @@ func (c *Cloner) deepCopy(src reflect.Value) (reflect.Value, error) {
} }
func (c *Cloner) customDeepCopy(src, fv reflect.Value) (reflect.Value, error) { func (c *Cloner) customDeepCopy(src, fv reflect.Value) (reflect.Value, error) {
outValue := reflect.New(src.Type()) outValue := reflect.New(src.Type().Elem())
args := []reflect.Value{src, outValue, reflect.ValueOf(c)} args := []reflect.Value{src, outValue, reflect.ValueOf(c)}
result := fv.Call(args)[0].Interface() result := fv.Call(args)[0].Interface()
// This convolution is necessary because nil interfaces won't convert // This convolution is necessary because nil interfaces won't convert
// to error. // to error.
if result == nil { if result == nil {
return outValue.Elem(), nil return outValue, nil
} }
return outValue.Elem(), result.(error) return outValue, result.(error)
} }
func (c *Cloner) defaultDeepCopy(src reflect.Value) (reflect.Value, error) { func (c *Cloner) defaultDeepCopy(src reflect.Value) (reflect.Value, error) {