golang.design/x/reflect@v0.0.0-20220504060917-02c43be63f3b/deepcopy.go (about)

     1  // Copyright 2022 The golang.design Initiative Authors.
     2  // All rights reserved. Use of this source code is governed
     3  // by a MIT license that can be found in the LICENSE file.
     4  //
     5  // Written by Changkun Ou <changkun.de>
     6  
     7  // Package reflect implements the proposal https://go.dev/issue/51520.
     8  //
     9  // Warning: Not largely tested. Use it with care.
    10  package reflect
    11  
    12  import (
    13  	"fmt"
    14  	"reflect"
    15  	"strings"
    16  	"unsafe"
    17  )
    18  
    19  // DeepCopyOption represents an option to customize deep copied results.
    20  type DeepCopyOption func(opt *copyConfig)
    21  
    22  type copyConfig struct {
    23  	disallowCopyUnexported        bool
    24  	disallowCopyCircular          bool
    25  	disallowCopyBidirectionalChan bool
    26  	disallowCopyTypes             []reflect.Type
    27  }
    28  
    29  // DisallowCopyUnexported returns a DeepCopyOption that disables the behavior
    30  // of copying unexported fields.
    31  func DisallowCopyUnexported() DeepCopyOption {
    32  	return func(opt *copyConfig) {
    33  		opt.disallowCopyUnexported = true
    34  	}
    35  }
    36  
    37  // DisallowCopyCircular returns a DeepCopyOption that disables the behavior
    38  // of copying circular structures.
    39  func DisallowCopyCircular() DeepCopyOption {
    40  	return func(opt *copyConfig) {
    41  		opt.disallowCopyCircular = true
    42  	}
    43  }
    44  
    45  // DisallowCopyBidirectionalChan returns a DeepCopyOption that disables
    46  // the behavior of producing new channel when a bidirectional channel is copied.
    47  func DisallowCopyBidirectionalChan() DeepCopyOption {
    48  	return func(opt *copyConfig) {
    49  		opt.disallowCopyBidirectionalChan = true
    50  	}
    51  }
    52  
    53  // DisallowTypes returns a DeepCopyOption that disallows copying any types
    54  // that are in given values.
    55  func DisallowTypes(val ...any) DeepCopyOption {
    56  	return func(opt *copyConfig) {
    57  		for i := range val {
    58  			opt.disallowCopyTypes = append(opt.disallowCopyTypes, reflect.TypeOf(val[i]))
    59  		}
    60  	}
    61  }
    62  
    63  // DeepCopy copies src to dst recursively.
    64  //
    65  // Two values of identical type are deeply copied if one of the following
    66  // cases apply.
    67  //
    68  // Numbers, bools, strings are deeply copied and have different underlying
    69  // memory address.
    70  //
    71  // Slice and Array values are deeply copied, including its elements.
    72  //
    73  // Map values are deeply copied for all of its key and corresponding
    74  // values.
    75  //
    76  // Pointer values are deeply copied for their pointed value, and the
    77  // pointer points to the deeply copied value.
    78  //
    79  // Struct values are deeply copied for all fields, including exported
    80  // and unexported.
    81  //
    82  // Interface values are deeply copied if the underlying type can be
    83  // deeply copied.
    84  //
    85  // There are a few exceptions that may result in a deeply copied value not
    86  // deeply equal (asserted by DeepEqual(dst, src)) to the source value:
    87  //
    88  // 1) Func values are still refer to the same function
    89  // 2) Chan values are replaced by newly created channels
    90  // 3) One-way Chan values (receive or read-only) values are still refer
    91  //    to the same channel
    92  //
    93  // Note that while correct uses of DeepCopy do exist, they are not rare.
    94  // The use of DeepCopy often indicates the copying object does not contain
    95  // a singleton or is never meant to be copied, such as sync.Mutex, os.File,
    96  // net.Conn, js.Value, etc. In these cases, the copied value retains the
    97  // memory representations of the source value but may result in unexpected
    98  // consequences in follow-up usage, the caller should clear these values
    99  // depending on their usage context.
   100  //
   101  // To change these predefined behaviors, use provided DeepCopyOption.
   102  func DeepCopy[T any](src T, opts ...DeepCopyOption) (dst T) {
   103  	ptrs := map[uintptr]any{}
   104  	conf := &copyConfig{}
   105  	for _, opt := range opts {
   106  		opt(conf)
   107  	}
   108  
   109  	ret := copyAny(src, ptrs, conf)
   110  	if v, ok := ret.(T); ok {
   111  		dst = v
   112  		return
   113  	}
   114  	panic(fmt.Sprintf("reflect: internal error: copied value is not typed in %T, got %T", src, ret))
   115  }
   116  
   117  func copyAny(src any, ptrs map[uintptr]any, copyConf *copyConfig) (dst any) {
   118  
   119  	if len(copyConf.disallowCopyTypes) != 0 {
   120  		for i := range copyConf.disallowCopyTypes {
   121  			if reflect.TypeOf(src) == copyConf.disallowCopyTypes[i] {
   122  				panic(fmt.Sprintf("reflect: deep copying type %T is disallowed", src))
   123  			}
   124  		}
   125  	}
   126  
   127  	v := reflect.ValueOf(src)
   128  	if !v.IsValid() {
   129  		return src
   130  	}
   131  
   132  	// Look up the corresponding copy function.
   133  	switch v.Kind() {
   134  	case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
   135  		reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
   136  		reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64,
   137  		reflect.Complex64, reflect.Complex128, reflect.Func:
   138  		dst = copyPremitive(src, ptrs, copyConf)
   139  	case reflect.String:
   140  		dst = strings.Clone(src.(string))
   141  	case reflect.Slice:
   142  		dst = copySlice(src, ptrs, copyConf)
   143  	case reflect.Array:
   144  		dst = copyArray(src, ptrs, copyConf)
   145  	case reflect.Map:
   146  		dst = copyMap(src, ptrs, copyConf)
   147  	case reflect.Ptr, reflect.UnsafePointer:
   148  		dst = copyPointer(src, ptrs, copyConf)
   149  	case reflect.Struct:
   150  		dst = copyStruct(src, ptrs, copyConf)
   151  	case reflect.Interface:
   152  		dst = copyAny(src, ptrs, copyConf)
   153  	case reflect.Chan:
   154  		dst = copyChan(src, ptrs, copyConf)
   155  	default:
   156  		panic(fmt.Sprintf("reflect: internal error: unknown type %v", v.Kind()))
   157  	}
   158  	return
   159  }
   160  
   161  func copyPremitive(src any, ptr map[uintptr]any, copyConf *copyConfig) (dst any) {
   162  	kind := reflect.ValueOf(src).Kind()
   163  	switch kind {
   164  	case reflect.Array, reflect.Chan, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice, reflect.Struct, reflect.UnsafePointer:
   165  		panic(fmt.Sprintf("reflect: internal error: type %v is not a primitive", kind))
   166  	}
   167  	dst = src
   168  	return
   169  }
   170  
   171  func copySlice(x any, ptrs map[uintptr]any, copyConf *copyConfig) any {
   172  	v := reflect.ValueOf(x)
   173  	kind := v.Kind()
   174  	if kind != reflect.Slice {
   175  		panic(fmt.Sprintf("reflect: internal error: type %v is not a slice", kind))
   176  	}
   177  
   178  	size := v.Len()
   179  	t := reflect.TypeOf(x)
   180  	dc := reflect.MakeSlice(t, size, size)
   181  	for i := 0; i < size; i++ {
   182  		iv := reflect.ValueOf(copyAny(v.Index(i).Interface(), ptrs, copyConf))
   183  		if iv.IsValid() {
   184  			dc.Index(i).Set(iv)
   185  		}
   186  	}
   187  	return dc.Interface()
   188  }
   189  
   190  func copyArray(x any, ptrs map[uintptr]any, copyConf *copyConfig) any {
   191  	v := reflect.ValueOf(x)
   192  	if v.Kind() != reflect.Array {
   193  		panic(fmt.Errorf("reflect: internal error: must be an Array; got %v", v.Kind()))
   194  	}
   195  	t := reflect.TypeOf(x)
   196  	size := t.Len()
   197  	dc := reflect.New(reflect.ArrayOf(size, t.Elem())).Elem()
   198  	for i := 0; i < size; i++ {
   199  		item := copyAny(v.Index(i).Interface(), ptrs, copyConf)
   200  		dc.Index(i).Set(reflect.ValueOf(item))
   201  	}
   202  	return dc.Interface()
   203  }
   204  
   205  func copyMap(x any, ptrs map[uintptr]any, copyConf *copyConfig) any {
   206  	v := reflect.ValueOf(x)
   207  	if v.Kind() != reflect.Map {
   208  		panic(fmt.Errorf("reflect: internal error: must be a Map; got %v", v.Kind()))
   209  	}
   210  	t := reflect.TypeOf(x)
   211  	dc := reflect.MakeMapWithSize(t, v.Len())
   212  	iter := v.MapRange()
   213  	for iter.Next() {
   214  		item := copyAny(iter.Value().Interface(), ptrs, copyConf)
   215  		k := copyAny(iter.Key().Interface(), ptrs, copyConf)
   216  		dc.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(item))
   217  	}
   218  	return dc.Interface()
   219  }
   220  
   221  func copyPointer(x any, ptrs map[uintptr]any, copyConf *copyConfig) any {
   222  	v := reflect.ValueOf(x)
   223  	if v.Kind() != reflect.Pointer {
   224  		panic(fmt.Errorf("reflect: internal error: must be a Pointer or Ptr; got %v", v.Kind()))
   225  	}
   226  	addr := uintptr(v.UnsafePointer())
   227  	if dc, ok := ptrs[addr]; ok {
   228  		if copyConf.disallowCopyCircular {
   229  			panic("reflect: deep copy dircular value is disallowed")
   230  		}
   231  		return dc
   232  	}
   233  	t := reflect.TypeOf(x)
   234  	dc := reflect.New(t.Elem())
   235  	ptrs[addr] = dc.Interface()
   236  	if !v.IsNil() {
   237  		item := copyAny(v.Elem().Interface(), ptrs, copyConf)
   238  		iv := reflect.ValueOf(item)
   239  		if iv.IsValid() {
   240  			dc.Elem().Set(reflect.ValueOf(item))
   241  		}
   242  	}
   243  	return dc.Interface()
   244  }
   245  
   246  func copyStruct(x any, ptrs map[uintptr]any, copyConf *copyConfig) any {
   247  	v := reflect.ValueOf(x)
   248  	if v.Kind() != reflect.Struct {
   249  		panic(fmt.Errorf("reflect: internal error: must be a Struct; got %v", v.Kind()))
   250  	}
   251  	t := reflect.TypeOf(x)
   252  	dc := reflect.New(t)
   253  	for i := 0; i < t.NumField(); i++ {
   254  		if copyConf.disallowCopyUnexported {
   255  			f := t.Field(i)
   256  			if f.PkgPath != "" {
   257  				continue
   258  			}
   259  			item := copyAny(v.Field(i).Interface(), ptrs, copyConf)
   260  			dc.Elem().Field(i).Set(reflect.ValueOf(item))
   261  		} else {
   262  			item := copyAny(valueInterfaceUnsafe(v.Field(i)), ptrs, copyConf)
   263  			setField(dc.Elem().Field(i), reflect.ValueOf(item))
   264  		}
   265  	}
   266  	return dc.Elem().Interface()
   267  }
   268  
   269  func copyChan(x any, ptrs map[uintptr]any, copyConf *copyConfig) any {
   270  	v := reflect.ValueOf(x)
   271  	if v.Kind() != reflect.Chan {
   272  		panic(fmt.Errorf("reflect: internal error: must be a Chan; got %v", v.Kind()))
   273  	}
   274  	t := reflect.TypeOf(x)
   275  	dir := t.ChanDir()
   276  	var dc any
   277  	switch dir {
   278  	case reflect.BothDir:
   279  		if !copyConf.disallowCopyBidirectionalChan {
   280  			dc = reflect.MakeChan(t, v.Cap()).Interface()
   281  		}
   282  		fallthrough
   283  	case reflect.SendDir, reflect.RecvDir:
   284  		dc = x
   285  	}
   286  	return dc
   287  }
   288  
   289  // valueInterfaceUnsafe overpasses the reflect package check regarding
   290  // unexported methods.
   291  func valueInterfaceUnsafe(v reflect.Value) any {
   292  	return reflect_valueInterface(v, false)
   293  }
   294  
   295  // setField sets the given value to the field value, regardless whether
   296  // the filed is exported or not.
   297  func setField(field reflect.Value, value reflect.Value) {
   298  	reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Set(value)
   299  }
   300  
   301  //go:linkname reflect_valueInterface reflect.valueInterface
   302  func reflect_valueInterface(v reflect.Value, safe bool) any