xref: /aosp_15_r20/build/blueprint/proptools/clone.go (revision 1fa6dee971e1612fa5cc0aa5ca2d35a22e2c34a3)
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