1*1fa6dee9SAndroid Build Coastguard Worker// Copyright 2014 Google Inc. All rights reserved. 2*1fa6dee9SAndroid Build Coastguard Worker// 3*1fa6dee9SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*1fa6dee9SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*1fa6dee9SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*1fa6dee9SAndroid Build Coastguard Worker// 7*1fa6dee9SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*1fa6dee9SAndroid Build Coastguard Worker// 9*1fa6dee9SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*1fa6dee9SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*1fa6dee9SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*1fa6dee9SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*1fa6dee9SAndroid Build Coastguard Worker// limitations under the License. 14*1fa6dee9SAndroid Build Coastguard Worker 15*1fa6dee9SAndroid Build Coastguard Workerpackage proptools 16*1fa6dee9SAndroid Build Coastguard Worker 17*1fa6dee9SAndroid Build Coastguard Workerimport ( 18*1fa6dee9SAndroid Build Coastguard Worker "fmt" 19*1fa6dee9SAndroid Build Coastguard Worker "reflect" 20*1fa6dee9SAndroid Build Coastguard Worker "sync" 21*1fa6dee9SAndroid Build Coastguard Worker) 22*1fa6dee9SAndroid Build Coastguard Worker 23*1fa6dee9SAndroid Build Coastguard Worker// CloneProperties takes a reflect.Value of a pointer to a struct and returns a reflect.Value 24*1fa6dee9SAndroid Build Coastguard Worker// of a pointer to a new struct that copies of the values for its fields. It recursively clones 25*1fa6dee9SAndroid Build Coastguard Worker// struct pointers and interfaces that contain struct pointers. 26*1fa6dee9SAndroid Build Coastguard Workerfunc CloneProperties(structValue reflect.Value) reflect.Value { 27*1fa6dee9SAndroid Build Coastguard Worker if !isStructPtr(structValue.Type()) { 28*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("CloneProperties expected *struct, got %s", structValue.Type())) 29*1fa6dee9SAndroid Build Coastguard Worker } 30*1fa6dee9SAndroid Build Coastguard Worker result := reflect.New(structValue.Type().Elem()) 31*1fa6dee9SAndroid Build Coastguard Worker copyProperties(result.Elem(), structValue.Elem()) 32*1fa6dee9SAndroid Build Coastguard Worker return result 33*1fa6dee9SAndroid Build Coastguard Worker} 34*1fa6dee9SAndroid Build Coastguard Worker 35*1fa6dee9SAndroid Build Coastguard Worker// CopyProperties takes destination and source reflect.Values of a pointer to structs and returns 36*1fa6dee9SAndroid Build Coastguard Worker// copies each field from the source into the destination. It recursively copies struct pointers 37*1fa6dee9SAndroid Build Coastguard Worker// and interfaces that contain struct pointers. 38*1fa6dee9SAndroid Build Coastguard Workerfunc CopyProperties(dstValue, srcValue reflect.Value) { 39*1fa6dee9SAndroid Build Coastguard Worker if !isStructPtr(dstValue.Type()) { 40*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("CopyProperties expected dstValue *struct, got %s", dstValue.Type())) 41*1fa6dee9SAndroid Build Coastguard Worker } 42*1fa6dee9SAndroid Build Coastguard Worker if !isStructPtr(srcValue.Type()) { 43*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("CopyProperties expected srcValue *struct, got %s", srcValue.Type())) 44*1fa6dee9SAndroid Build Coastguard Worker } 45*1fa6dee9SAndroid Build Coastguard Worker copyProperties(dstValue.Elem(), srcValue.Elem()) 46*1fa6dee9SAndroid Build Coastguard Worker} 47*1fa6dee9SAndroid Build Coastguard Worker 48*1fa6dee9SAndroid Build Coastguard Workerfunc copyProperties(dstValue, srcValue reflect.Value) { 49*1fa6dee9SAndroid Build Coastguard Worker typ := dstValue.Type() 50*1fa6dee9SAndroid Build Coastguard Worker if srcValue.Type() != typ { 51*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("can't copy mismatching types (%s <- %s)", 52*1fa6dee9SAndroid Build Coastguard Worker dstValue.Kind(), srcValue.Kind())) 53*1fa6dee9SAndroid Build Coastguard Worker } 54*1fa6dee9SAndroid Build Coastguard Worker 55*1fa6dee9SAndroid Build Coastguard Worker for i, field := range typeFields(typ) { 56*1fa6dee9SAndroid Build Coastguard Worker if field.PkgPath != "" { 57*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("can't copy a private field %q", field.Name)) 58*1fa6dee9SAndroid Build Coastguard Worker } 59*1fa6dee9SAndroid Build Coastguard Worker 60*1fa6dee9SAndroid Build Coastguard Worker srcFieldValue := srcValue.Field(i) 61*1fa6dee9SAndroid Build Coastguard Worker dstFieldValue := dstValue.Field(i) 62*1fa6dee9SAndroid Build Coastguard Worker dstFieldInterfaceValue := reflect.Value{} 63*1fa6dee9SAndroid Build Coastguard Worker origDstFieldValue := dstFieldValue 64*1fa6dee9SAndroid Build Coastguard Worker 65*1fa6dee9SAndroid Build Coastguard Worker switch srcFieldValue.Kind() { 66*1fa6dee9SAndroid Build Coastguard Worker case reflect.Bool, reflect.String, reflect.Int, reflect.Uint: 67*1fa6dee9SAndroid Build Coastguard Worker dstFieldValue.Set(srcFieldValue) 68*1fa6dee9SAndroid Build Coastguard Worker case reflect.Struct: 69*1fa6dee9SAndroid Build Coastguard Worker if isConfigurable(srcFieldValue.Type()) { 70*1fa6dee9SAndroid Build Coastguard Worker dstFieldValue.Set(reflect.ValueOf(srcFieldValue.Interface().(configurableReflection).clone())) 71*1fa6dee9SAndroid Build Coastguard Worker } else { 72*1fa6dee9SAndroid Build Coastguard Worker copyProperties(dstFieldValue, srcFieldValue) 73*1fa6dee9SAndroid Build Coastguard Worker } 74*1fa6dee9SAndroid Build Coastguard Worker case reflect.Slice: 75*1fa6dee9SAndroid Build Coastguard Worker if !srcFieldValue.IsNil() { 76*1fa6dee9SAndroid Build Coastguard Worker if srcFieldValue != dstFieldValue { 77*1fa6dee9SAndroid Build Coastguard Worker newSlice := reflect.MakeSlice(field.Type, srcFieldValue.Len(), 78*1fa6dee9SAndroid Build Coastguard Worker srcFieldValue.Len()) 79*1fa6dee9SAndroid Build Coastguard Worker reflect.Copy(newSlice, srcFieldValue) 80*1fa6dee9SAndroid Build Coastguard Worker dstFieldValue.Set(newSlice) 81*1fa6dee9SAndroid Build Coastguard Worker } 82*1fa6dee9SAndroid Build Coastguard Worker } else { 83*1fa6dee9SAndroid Build Coastguard Worker dstFieldValue.Set(srcFieldValue) 84*1fa6dee9SAndroid Build Coastguard Worker } 85*1fa6dee9SAndroid Build Coastguard Worker case reflect.Map: 86*1fa6dee9SAndroid Build Coastguard Worker if !srcFieldValue.IsNil() { 87*1fa6dee9SAndroid Build Coastguard Worker newMap := reflect.MakeMap(field.Type) 88*1fa6dee9SAndroid Build Coastguard Worker 89*1fa6dee9SAndroid Build Coastguard Worker iter := srcFieldValue.MapRange() 90*1fa6dee9SAndroid Build Coastguard Worker for iter.Next() { 91*1fa6dee9SAndroid Build Coastguard Worker newMap.SetMapIndex(iter.Key(), iter.Value()) 92*1fa6dee9SAndroid Build Coastguard Worker } 93*1fa6dee9SAndroid Build Coastguard Worker dstFieldValue.Set(newMap) 94*1fa6dee9SAndroid Build Coastguard Worker } else { 95*1fa6dee9SAndroid Build Coastguard Worker dstFieldValue.Set(srcFieldValue) 96*1fa6dee9SAndroid Build Coastguard Worker } 97*1fa6dee9SAndroid Build Coastguard Worker case reflect.Interface: 98*1fa6dee9SAndroid Build Coastguard Worker if srcFieldValue.IsNil() { 99*1fa6dee9SAndroid Build Coastguard Worker dstFieldValue.Set(srcFieldValue) 100*1fa6dee9SAndroid Build Coastguard Worker break 101*1fa6dee9SAndroid Build Coastguard Worker } 102*1fa6dee9SAndroid Build Coastguard Worker 103*1fa6dee9SAndroid Build Coastguard Worker srcFieldValue = srcFieldValue.Elem() 104*1fa6dee9SAndroid Build Coastguard Worker 105*1fa6dee9SAndroid Build Coastguard Worker if !isStructPtr(srcFieldValue.Type()) { 106*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("can't clone field %q: expected interface to contain *struct, found %s", 107*1fa6dee9SAndroid Build Coastguard Worker field.Name, srcFieldValue.Type())) 108*1fa6dee9SAndroid Build Coastguard Worker } 109*1fa6dee9SAndroid Build Coastguard Worker 110*1fa6dee9SAndroid Build Coastguard Worker if dstFieldValue.IsNil() || dstFieldValue.Elem().Type() != srcFieldValue.Type() { 111*1fa6dee9SAndroid Build Coastguard Worker // We can't use the existing destination allocation, so 112*1fa6dee9SAndroid Build Coastguard Worker // clone a new one. 113*1fa6dee9SAndroid Build Coastguard Worker newValue := reflect.New(srcFieldValue.Type()).Elem() 114*1fa6dee9SAndroid Build Coastguard Worker dstFieldValue.Set(newValue) 115*1fa6dee9SAndroid Build Coastguard Worker dstFieldInterfaceValue = dstFieldValue 116*1fa6dee9SAndroid Build Coastguard Worker dstFieldValue = newValue 117*1fa6dee9SAndroid Build Coastguard Worker } else { 118*1fa6dee9SAndroid Build Coastguard Worker dstFieldValue = dstFieldValue.Elem() 119*1fa6dee9SAndroid Build Coastguard Worker } 120*1fa6dee9SAndroid Build Coastguard Worker fallthrough 121*1fa6dee9SAndroid Build Coastguard Worker case reflect.Ptr: 122*1fa6dee9SAndroid Build Coastguard Worker if srcFieldValue.IsNil() { 123*1fa6dee9SAndroid Build Coastguard Worker origDstFieldValue.Set(srcFieldValue) 124*1fa6dee9SAndroid Build Coastguard Worker break 125*1fa6dee9SAndroid Build Coastguard Worker } 126*1fa6dee9SAndroid Build Coastguard Worker 127*1fa6dee9SAndroid Build Coastguard Worker switch srcFieldValue.Elem().Kind() { 128*1fa6dee9SAndroid Build Coastguard Worker case reflect.Struct: 129*1fa6dee9SAndroid Build Coastguard Worker if !dstFieldValue.IsNil() { 130*1fa6dee9SAndroid Build Coastguard Worker // Re-use the existing allocation. 131*1fa6dee9SAndroid Build Coastguard Worker copyProperties(dstFieldValue.Elem(), srcFieldValue.Elem()) 132*1fa6dee9SAndroid Build Coastguard Worker break 133*1fa6dee9SAndroid Build Coastguard Worker } else { 134*1fa6dee9SAndroid Build Coastguard Worker newValue := CloneProperties(srcFieldValue) 135*1fa6dee9SAndroid Build Coastguard Worker if dstFieldInterfaceValue.IsValid() { 136*1fa6dee9SAndroid Build Coastguard Worker dstFieldInterfaceValue.Set(newValue) 137*1fa6dee9SAndroid Build Coastguard Worker } else { 138*1fa6dee9SAndroid Build Coastguard Worker origDstFieldValue.Set(newValue) 139*1fa6dee9SAndroid Build Coastguard Worker } 140*1fa6dee9SAndroid Build Coastguard Worker } 141*1fa6dee9SAndroid Build Coastguard Worker case reflect.Bool, reflect.Int64, reflect.String: 142*1fa6dee9SAndroid Build Coastguard Worker newValue := reflect.New(srcFieldValue.Elem().Type()) 143*1fa6dee9SAndroid Build Coastguard Worker newValue.Elem().Set(srcFieldValue.Elem()) 144*1fa6dee9SAndroid Build Coastguard Worker origDstFieldValue.Set(newValue) 145*1fa6dee9SAndroid Build Coastguard Worker default: 146*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("can't clone pointer field %q type %s", 147*1fa6dee9SAndroid Build Coastguard Worker field.Name, srcFieldValue.Type())) 148*1fa6dee9SAndroid Build Coastguard Worker } 149*1fa6dee9SAndroid Build Coastguard Worker default: 150*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("unexpected type for property struct field %q: %s", 151*1fa6dee9SAndroid Build Coastguard Worker field.Name, srcFieldValue.Type())) 152*1fa6dee9SAndroid Build Coastguard Worker } 153*1fa6dee9SAndroid Build Coastguard Worker } 154*1fa6dee9SAndroid Build Coastguard Worker} 155*1fa6dee9SAndroid Build Coastguard Worker 156*1fa6dee9SAndroid Build Coastguard Worker// ZeroProperties takes a reflect.Value of a pointer to a struct and replaces all of its fields 157*1fa6dee9SAndroid Build Coastguard Worker// with zero values, recursing into struct, pointer to struct and interface fields. 158*1fa6dee9SAndroid Build Coastguard Workerfunc ZeroProperties(structValue reflect.Value) { 159*1fa6dee9SAndroid Build Coastguard Worker if !isStructPtr(structValue.Type()) { 160*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("ZeroProperties expected *struct, got %s", structValue.Type())) 161*1fa6dee9SAndroid Build Coastguard Worker } 162*1fa6dee9SAndroid Build Coastguard Worker zeroProperties(structValue.Elem()) 163*1fa6dee9SAndroid Build Coastguard Worker} 164*1fa6dee9SAndroid Build Coastguard Worker 165*1fa6dee9SAndroid Build Coastguard Workerfunc zeroProperties(structValue reflect.Value) { 166*1fa6dee9SAndroid Build Coastguard Worker typ := structValue.Type() 167*1fa6dee9SAndroid Build Coastguard Worker 168*1fa6dee9SAndroid Build Coastguard Worker for i, field := range typeFields(typ) { 169*1fa6dee9SAndroid Build Coastguard Worker if field.PkgPath != "" { 170*1fa6dee9SAndroid Build Coastguard Worker // The field is not exported so just skip it. 171*1fa6dee9SAndroid Build Coastguard Worker continue 172*1fa6dee9SAndroid Build Coastguard Worker } 173*1fa6dee9SAndroid Build Coastguard Worker 174*1fa6dee9SAndroid Build Coastguard Worker fieldValue := structValue.Field(i) 175*1fa6dee9SAndroid Build Coastguard Worker 176*1fa6dee9SAndroid Build Coastguard Worker switch fieldValue.Kind() { 177*1fa6dee9SAndroid Build Coastguard Worker case reflect.Bool, reflect.String, reflect.Slice, reflect.Int, reflect.Uint, reflect.Map: 178*1fa6dee9SAndroid Build Coastguard Worker fieldValue.Set(reflect.Zero(fieldValue.Type())) 179*1fa6dee9SAndroid Build Coastguard Worker case reflect.Interface: 180*1fa6dee9SAndroid Build Coastguard Worker if fieldValue.IsNil() { 181*1fa6dee9SAndroid Build Coastguard Worker break 182*1fa6dee9SAndroid Build Coastguard Worker } 183*1fa6dee9SAndroid Build Coastguard Worker 184*1fa6dee9SAndroid Build Coastguard Worker // We leave the pointer intact and zero out the struct that's 185*1fa6dee9SAndroid Build Coastguard Worker // pointed to. 186*1fa6dee9SAndroid Build Coastguard Worker fieldValue = fieldValue.Elem() 187*1fa6dee9SAndroid Build Coastguard Worker if !isStructPtr(fieldValue.Type()) { 188*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("can't zero field %q: expected interface to contain *struct, found %s", 189*1fa6dee9SAndroid Build Coastguard Worker field.Name, fieldValue.Type())) 190*1fa6dee9SAndroid Build Coastguard Worker } 191*1fa6dee9SAndroid Build Coastguard Worker fallthrough 192*1fa6dee9SAndroid Build Coastguard Worker case reflect.Ptr: 193*1fa6dee9SAndroid Build Coastguard Worker switch fieldValue.Type().Elem().Kind() { 194*1fa6dee9SAndroid Build Coastguard Worker case reflect.Struct: 195*1fa6dee9SAndroid Build Coastguard Worker if fieldValue.IsNil() { 196*1fa6dee9SAndroid Build Coastguard Worker break 197*1fa6dee9SAndroid Build Coastguard Worker } 198*1fa6dee9SAndroid Build Coastguard Worker zeroProperties(fieldValue.Elem()) 199*1fa6dee9SAndroid Build Coastguard Worker case reflect.Bool, reflect.Int64, reflect.String: 200*1fa6dee9SAndroid Build Coastguard Worker fieldValue.Set(reflect.Zero(fieldValue.Type())) 201*1fa6dee9SAndroid Build Coastguard Worker default: 202*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("can't zero field %q: points to a %s", 203*1fa6dee9SAndroid Build Coastguard Worker field.Name, fieldValue.Elem().Kind())) 204*1fa6dee9SAndroid Build Coastguard Worker } 205*1fa6dee9SAndroid Build Coastguard Worker case reflect.Struct: 206*1fa6dee9SAndroid Build Coastguard Worker zeroProperties(fieldValue) 207*1fa6dee9SAndroid Build Coastguard Worker default: 208*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("unexpected kind for property struct field %q: %s", 209*1fa6dee9SAndroid Build Coastguard Worker field.Name, fieldValue.Kind())) 210*1fa6dee9SAndroid Build Coastguard Worker } 211*1fa6dee9SAndroid Build Coastguard Worker } 212*1fa6dee9SAndroid Build Coastguard Worker} 213*1fa6dee9SAndroid Build Coastguard Worker 214*1fa6dee9SAndroid Build Coastguard Worker// CloneEmptyProperties takes a reflect.Value of a pointer to a struct and returns a reflect.Value 215*1fa6dee9SAndroid Build Coastguard Worker// of a pointer to a new struct that has the zero values for its fields. It recursively clones 216*1fa6dee9SAndroid Build Coastguard Worker// struct pointers and interfaces that contain struct pointers. 217*1fa6dee9SAndroid Build Coastguard Workerfunc CloneEmptyProperties(structValue reflect.Value) reflect.Value { 218*1fa6dee9SAndroid Build Coastguard Worker if !isStructPtr(structValue.Type()) { 219*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("CloneEmptyProperties expected *struct, got %s", structValue.Type())) 220*1fa6dee9SAndroid Build Coastguard Worker } 221*1fa6dee9SAndroid Build Coastguard Worker result := reflect.New(structValue.Type().Elem()) 222*1fa6dee9SAndroid Build Coastguard Worker cloneEmptyProperties(result.Elem(), structValue.Elem()) 223*1fa6dee9SAndroid Build Coastguard Worker return result 224*1fa6dee9SAndroid Build Coastguard Worker} 225*1fa6dee9SAndroid Build Coastguard Worker 226*1fa6dee9SAndroid Build Coastguard Workerfunc cloneEmptyProperties(dstValue, srcValue reflect.Value) { 227*1fa6dee9SAndroid Build Coastguard Worker typ := srcValue.Type() 228*1fa6dee9SAndroid Build Coastguard Worker for i, field := range typeFields(typ) { 229*1fa6dee9SAndroid Build Coastguard Worker if field.PkgPath != "" { 230*1fa6dee9SAndroid Build Coastguard Worker // The field is not exported so just skip it. 231*1fa6dee9SAndroid Build Coastguard Worker continue 232*1fa6dee9SAndroid Build Coastguard Worker } 233*1fa6dee9SAndroid Build Coastguard Worker 234*1fa6dee9SAndroid Build Coastguard Worker srcFieldValue := srcValue.Field(i) 235*1fa6dee9SAndroid Build Coastguard Worker dstFieldValue := dstValue.Field(i) 236*1fa6dee9SAndroid Build Coastguard Worker dstFieldInterfaceValue := reflect.Value{} 237*1fa6dee9SAndroid Build Coastguard Worker 238*1fa6dee9SAndroid Build Coastguard Worker switch srcFieldValue.Kind() { 239*1fa6dee9SAndroid Build Coastguard Worker case reflect.Bool, reflect.String, reflect.Slice, reflect.Map, reflect.Int, reflect.Uint: 240*1fa6dee9SAndroid Build Coastguard Worker // Nothing 241*1fa6dee9SAndroid Build Coastguard Worker case reflect.Struct: 242*1fa6dee9SAndroid Build Coastguard Worker cloneEmptyProperties(dstFieldValue, srcFieldValue) 243*1fa6dee9SAndroid Build Coastguard Worker case reflect.Interface: 244*1fa6dee9SAndroid Build Coastguard Worker if srcFieldValue.IsNil() { 245*1fa6dee9SAndroid Build Coastguard Worker break 246*1fa6dee9SAndroid Build Coastguard Worker } 247*1fa6dee9SAndroid Build Coastguard Worker 248*1fa6dee9SAndroid Build Coastguard Worker srcFieldValue = srcFieldValue.Elem() 249*1fa6dee9SAndroid Build Coastguard Worker if !isStructPtr(srcFieldValue.Type()) { 250*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("can't clone empty field %q: expected interface to contain *struct, found %s", 251*1fa6dee9SAndroid Build Coastguard Worker field.Name, srcFieldValue.Type())) 252*1fa6dee9SAndroid Build Coastguard Worker } 253*1fa6dee9SAndroid Build Coastguard Worker 254*1fa6dee9SAndroid Build Coastguard Worker newValue := reflect.New(srcFieldValue.Type()).Elem() 255*1fa6dee9SAndroid Build Coastguard Worker dstFieldValue.Set(newValue) 256*1fa6dee9SAndroid Build Coastguard Worker dstFieldInterfaceValue = dstFieldValue 257*1fa6dee9SAndroid Build Coastguard Worker dstFieldValue = newValue 258*1fa6dee9SAndroid Build Coastguard Worker fallthrough 259*1fa6dee9SAndroid Build Coastguard Worker case reflect.Ptr: 260*1fa6dee9SAndroid Build Coastguard Worker switch srcFieldValue.Type().Elem().Kind() { 261*1fa6dee9SAndroid Build Coastguard Worker case reflect.Struct: 262*1fa6dee9SAndroid Build Coastguard Worker if srcFieldValue.IsNil() { 263*1fa6dee9SAndroid Build Coastguard Worker break 264*1fa6dee9SAndroid Build Coastguard Worker } 265*1fa6dee9SAndroid Build Coastguard Worker newValue := CloneEmptyProperties(srcFieldValue) 266*1fa6dee9SAndroid Build Coastguard Worker if dstFieldInterfaceValue.IsValid() { 267*1fa6dee9SAndroid Build Coastguard Worker dstFieldInterfaceValue.Set(newValue) 268*1fa6dee9SAndroid Build Coastguard Worker } else { 269*1fa6dee9SAndroid Build Coastguard Worker dstFieldValue.Set(newValue) 270*1fa6dee9SAndroid Build Coastguard Worker } 271*1fa6dee9SAndroid Build Coastguard Worker case reflect.Bool, reflect.Int64, reflect.String: 272*1fa6dee9SAndroid Build Coastguard Worker // Nothing 273*1fa6dee9SAndroid Build Coastguard Worker default: 274*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("can't clone empty field %q: points to a %s", 275*1fa6dee9SAndroid Build Coastguard Worker field.Name, srcFieldValue.Elem().Kind())) 276*1fa6dee9SAndroid Build Coastguard Worker } 277*1fa6dee9SAndroid Build Coastguard Worker 278*1fa6dee9SAndroid Build Coastguard Worker default: 279*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("unexpected kind for property struct field %q: %s", 280*1fa6dee9SAndroid Build Coastguard Worker field.Name, srcFieldValue.Kind())) 281*1fa6dee9SAndroid Build Coastguard Worker } 282*1fa6dee9SAndroid Build Coastguard Worker } 283*1fa6dee9SAndroid Build Coastguard Worker} 284*1fa6dee9SAndroid Build Coastguard Worker 285*1fa6dee9SAndroid Build Coastguard Workervar typeFieldCache sync.Map 286*1fa6dee9SAndroid Build Coastguard Worker 287*1fa6dee9SAndroid Build Coastguard Workerfunc typeFields(typ reflect.Type) []reflect.StructField { 288*1fa6dee9SAndroid Build Coastguard Worker // reflect.Type.Field allocates a []int{} to hold the index every time it is called, which ends up 289*1fa6dee9SAndroid Build Coastguard Worker // being a significant portion of the GC pressure. It can't reuse the same one in case a caller 290*1fa6dee9SAndroid Build Coastguard Worker // modifies the backing array through the slice. Since we don't modify it, cache the result 291*1fa6dee9SAndroid Build Coastguard Worker // locally to reduce allocations. 292*1fa6dee9SAndroid Build Coastguard Worker 293*1fa6dee9SAndroid Build Coastguard Worker // Fast path 294*1fa6dee9SAndroid Build Coastguard Worker if typeFields, ok := typeFieldCache.Load(typ); ok { 295*1fa6dee9SAndroid Build Coastguard Worker return typeFields.([]reflect.StructField) 296*1fa6dee9SAndroid Build Coastguard Worker } 297*1fa6dee9SAndroid Build Coastguard Worker 298*1fa6dee9SAndroid Build Coastguard Worker // Slow path 299*1fa6dee9SAndroid Build Coastguard Worker typeFields := make([]reflect.StructField, typ.NumField()) 300*1fa6dee9SAndroid Build Coastguard Worker 301*1fa6dee9SAndroid Build Coastguard Worker for i := range typeFields { 302*1fa6dee9SAndroid Build Coastguard Worker typeFields[i] = typ.Field(i) 303*1fa6dee9SAndroid Build Coastguard Worker } 304*1fa6dee9SAndroid Build Coastguard Worker 305*1fa6dee9SAndroid Build Coastguard Worker typeFieldCache.Store(typ, typeFields) 306*1fa6dee9SAndroid Build Coastguard Worker 307*1fa6dee9SAndroid Build Coastguard Worker return typeFields 308*1fa6dee9SAndroid Build Coastguard Worker} 309