1// Copyright 2015 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package android
16
17import (
18	"fmt"
19	"reflect"
20	"runtime"
21	"strings"
22
23	"github.com/google/blueprint/proptools"
24)
25
26func init() {
27	PreDepsMutators(func(ctx RegisterMutatorsContext) {
28		ctx.BottomUp("variable", VariableMutator).Parallel()
29	})
30}
31
32type variableProperties struct {
33	Product_variables struct {
34		Platform_sdk_version struct {
35			Asflags []string
36			Cflags  []string
37		}
38
39		// unbundled_build is a catch-all property to annotate modules that don't build in one or
40		// more unbundled branches, usually due to dependencies missing from the manifest.
41		Unbundled_build struct {
42			Enabled *bool `android:"arch_variant"`
43		} `android:"arch_variant"`
44
45		Malloc_not_svelte struct {
46			Cflags              []string `android:"arch_variant"`
47			Shared_libs         []string `android:"arch_variant"`
48			Whole_static_libs   []string `android:"arch_variant"`
49			Exclude_static_libs []string `android:"arch_variant"`
50		} `android:"arch_variant"`
51
52		Malloc_zero_contents struct {
53			Cflags []string `android:"arch_variant"`
54		} `android:"arch_variant"`
55
56		Malloc_pattern_fill_contents struct {
57			Cflags []string `android:"arch_variant"`
58		} `android:"arch_variant"`
59
60		Safestack struct {
61			Cflags []string `android:"arch_variant"`
62		} `android:"arch_variant"`
63
64		Binder32bit struct {
65			Cflags []string
66		}
67
68		Override_rs_driver struct {
69			Cflags []string
70		}
71
72		// treble_linker_namespaces is true when the system/vendor linker namespace separation is
73		// enabled.
74		Treble_linker_namespaces struct {
75			Cflags []string
76		}
77		// enforce_vintf_manifest is true when a device is required to have a vintf manifest.
78		Enforce_vintf_manifest struct {
79			Cflags []string
80		}
81
82		// debuggable is true for eng and userdebug builds, and can be used to turn on additional
83		// debugging features that don't significantly impact runtime behavior.  userdebug builds
84		// are used for dogfooding and performance testing, and should be as similar to user builds
85		// as possible.
86		Debuggable struct {
87			Cflags          []string
88			Cppflags        []string
89			Init_rc         []string
90			Required        []string
91			Host_required   []string
92			Target_required []string
93		}
94
95		// eng is true for -eng builds, and can be used to turn on additionaly heavyweight debugging
96		// features.
97		Eng struct {
98			Cflags   []string
99			Cppflags []string
100			Lto      struct {
101				Never *bool
102			}
103			Sanitize struct {
104				Address *bool
105			}
106			Optimize struct {
107				Enabled *bool
108			}
109		}
110
111		Pdk struct {
112			Enabled *bool `android:"arch_variant"`
113		} `android:"arch_variant"`
114
115		Uml struct {
116			Cppflags []string
117		}
118
119		Use_lmkd_stats_log struct {
120			Cflags []string
121		}
122
123		Arc struct {
124			Cflags       []string
125			Exclude_srcs []string
126			Include_dirs []string
127			Shared_libs  []string
128			Static_libs  []string
129			Srcs         []string
130		}
131
132		Flatten_apex struct {
133			Enabled *bool
134		}
135
136		Experimental_mte struct {
137			Cflags []string `android:"arch_variant"`
138		} `android:"arch_variant"`
139
140		Native_coverage struct {
141			Src          *string  `android:"arch_variant"`
142			Srcs         []string `android:"arch_variant"`
143			Exclude_srcs []string `android:"arch_variant"`
144		} `android:"arch_variant"`
145	} `android:"arch_variant"`
146}
147
148var defaultProductVariables interface{} = variableProperties{}
149
150type productVariables struct {
151	// Suffix to add to generated Makefiles
152	Make_suffix *string `json:",omitempty"`
153
154	BuildId         *string `json:",omitempty"`
155	BuildNumberFile *string `json:",omitempty"`
156
157	Platform_version_name                     *string  `json:",omitempty"`
158	Platform_sdk_version                      *int     `json:",omitempty"`
159	Platform_sdk_codename                     *string  `json:",omitempty"`
160	Platform_sdk_final                        *bool    `json:",omitempty"`
161	Platform_version_active_codenames         []string `json:",omitempty"`
162	Platform_vndk_version                     *string  `json:",omitempty"`
163	Platform_systemsdk_versions               []string `json:",omitempty"`
164	Platform_security_patch                   *string  `json:",omitempty"`
165	Platform_preview_sdk_version              *string  `json:",omitempty"`
166	Platform_min_supported_target_sdk_version *string  `json:",omitempty"`
167	Platform_base_os                          *string  `json:",omitempty"`
168
169	DeviceName              *string  `json:",omitempty"`
170	DeviceArch              *string  `json:",omitempty"`
171	DeviceArchVariant       *string  `json:",omitempty"`
172	DeviceCpuVariant        *string  `json:",omitempty"`
173	DeviceAbi               []string `json:",omitempty"`
174	DeviceVndkVersion       *string  `json:",omitempty"`
175	DeviceSystemSdkVersions []string `json:",omitempty"`
176
177	DeviceSecondaryArch        *string  `json:",omitempty"`
178	DeviceSecondaryArchVariant *string  `json:",omitempty"`
179	DeviceSecondaryCpuVariant  *string  `json:",omitempty"`
180	DeviceSecondaryAbi         []string `json:",omitempty"`
181
182	NativeBridgeArch         *string  `json:",omitempty"`
183	NativeBridgeArchVariant  *string  `json:",omitempty"`
184	NativeBridgeCpuVariant   *string  `json:",omitempty"`
185	NativeBridgeAbi          []string `json:",omitempty"`
186	NativeBridgeRelativePath *string  `json:",omitempty"`
187
188	NativeBridgeSecondaryArch         *string  `json:",omitempty"`
189	NativeBridgeSecondaryArchVariant  *string  `json:",omitempty"`
190	NativeBridgeSecondaryCpuVariant   *string  `json:",omitempty"`
191	NativeBridgeSecondaryAbi          []string `json:",omitempty"`
192	NativeBridgeSecondaryRelativePath *string  `json:",omitempty"`
193
194	HostArch          *string `json:",omitempty"`
195	HostSecondaryArch *string `json:",omitempty"`
196
197	CrossHost              *string `json:",omitempty"`
198	CrossHostArch          *string `json:",omitempty"`
199	CrossHostSecondaryArch *string `json:",omitempty"`
200
201	DeviceResourceOverlays  []string `json:",omitempty"`
202	ProductResourceOverlays []string `json:",omitempty"`
203	EnforceRROTargets       []string `json:",omitempty"`
204	// TODO(b/150820813) Some modules depend on static overlay, remove this after eliminating the dependency.
205	EnforceRROExemptedTargets  []string `json:",omitempty"`
206	EnforceRROExcludedOverlays []string `json:",omitempty"`
207
208	AAPTCharacteristics *string  `json:",omitempty"`
209	AAPTConfig          []string `json:",omitempty"`
210	AAPTPreferredConfig *string  `json:",omitempty"`
211	AAPTPrebuiltDPI     []string `json:",omitempty"`
212
213	DefaultAppCertificate *string `json:",omitempty"`
214
215	AppsDefaultVersionName *string `json:",omitempty"`
216
217	Allow_missing_dependencies       *bool `json:",omitempty"`
218	Unbundled_build                  *bool `json:",omitempty"`
219	Unbundled_build_apps             *bool `json:",omitempty"`
220	Unbundled_build_sdks_from_source *bool `json:",omitempty"`
221	Malloc_not_svelte                *bool `json:",omitempty"`
222	Malloc_zero_contents             *bool `json:",omitempty"`
223	Malloc_pattern_fill_contents     *bool `json:",omitempty"`
224	Safestack                        *bool `json:",omitempty"`
225	HostStaticBinaries               *bool `json:",omitempty"`
226	Binder32bit                      *bool `json:",omitempty"`
227	UseGoma                          *bool `json:",omitempty"`
228	UseRBE                           *bool `json:",omitempty"`
229	UseRBEJAVAC                      *bool `json:",omitempty"`
230	UseRBER8                         *bool `json:",omitempty"`
231	UseRBED8                         *bool `json:",omitempty"`
232	Debuggable                       *bool `json:",omitempty"`
233	Eng                              *bool `json:",omitempty"`
234	Treble_linker_namespaces         *bool `json:",omitempty"`
235	Enforce_vintf_manifest           *bool `json:",omitempty"`
236	Pdk                              *bool `json:",omitempty"`
237	Uml                              *bool `json:",omitempty"`
238	Use_lmkd_stats_log               *bool `json:",omitempty"`
239	Arc                              *bool `json:",omitempty"`
240	MinimizeJavaDebugInfo            *bool `json:",omitempty"`
241
242	Check_elf_files *bool `json:",omitempty"`
243
244	UncompressPrivAppDex             *bool    `json:",omitempty"`
245	ModulesLoadedByPrivilegedModules []string `json:",omitempty"`
246
247	BootJars          []string `json:",omitempty"`
248	UpdatableBootJars []string `json:",omitempty"`
249
250	IntegerOverflowExcludePaths []string `json:",omitempty"`
251
252	EnableCFI       *bool    `json:",omitempty"`
253	CFIExcludePaths []string `json:",omitempty"`
254	CFIIncludePaths []string `json:",omitempty"`
255
256	DisableScudo *bool `json:",omitempty"`
257
258	Experimental_mte *bool `json:",omitempty"`
259
260	VendorPath    *string `json:",omitempty"`
261	OdmPath       *string `json:",omitempty"`
262	ProductPath   *string `json:",omitempty"`
263	SystemExtPath *string `json:",omitempty"`
264
265	ClangTidy  *bool   `json:",omitempty"`
266	TidyChecks *string `json:",omitempty"`
267
268	SamplingPGO *bool `json:",omitempty"`
269
270	JavaCoveragePaths        []string `json:",omitempty"`
271	JavaCoverageExcludePaths []string `json:",omitempty"`
272
273	GcovCoverage               *bool    `json:",omitempty"`
274	ClangCoverage              *bool    `json:",omitempty"`
275	NativeCoveragePaths        []string `json:",omitempty"`
276	NativeCoverageExcludePaths []string `json:",omitempty"`
277
278	// Set by NewConfig
279	Native_coverage *bool
280
281	SanitizeHost       []string `json:",omitempty"`
282	SanitizeDevice     []string `json:",omitempty"`
283	SanitizeDeviceDiag []string `json:",omitempty"`
284	SanitizeDeviceArch []string `json:",omitempty"`
285
286	ArtUseReadBarrier *bool `json:",omitempty"`
287
288	BtConfigIncludeDir *string `json:",omitempty"`
289
290	Override_rs_driver *string `json:",omitempty"`
291
292	Fuchsia *bool `json:",omitempty"`
293
294	DeviceKernelHeaders []string `json:",omitempty"`
295
296	ExtraVndkVersions []string `json:",omitempty"`
297
298	NamespacesToExport []string `json:",omitempty"`
299
300	PgoAdditionalProfileDirs []string `json:",omitempty"`
301
302	VndkUseCoreVariant         *bool `json:",omitempty"`
303	VndkSnapshotBuildArtifacts *bool `json:",omitempty"`
304
305	BoardVendorSepolicyDirs      []string `json:",omitempty"`
306	BoardOdmSepolicyDirs         []string `json:",omitempty"`
307	BoardPlatPublicSepolicyDirs  []string `json:",omitempty"`
308	BoardPlatPrivateSepolicyDirs []string `json:",omitempty"`
309	BoardSepolicyM4Defs          []string `json:",omitempty"`
310
311	VendorVars map[string]map[string]string `json:",omitempty"`
312
313	Ndk_abis               *bool `json:",omitempty"`
314	Exclude_draft_ndk_apis *bool `json:",omitempty"`
315
316	Flatten_apex *bool `json:",omitempty"`
317	Aml_abis     *bool `json:",omitempty"`
318
319	DexpreoptGlobalConfig *string `json:",omitempty"`
320
321	ManifestPackageNameOverrides []string `json:",omitempty"`
322	CertificateOverrides         []string `json:",omitempty"`
323	PackageNameOverrides         []string `json:",omitempty"`
324
325	EnforceSystemCertificate          *bool    `json:",omitempty"`
326	EnforceSystemCertificateAllowList []string `json:",omitempty"`
327
328	ProductHiddenAPIStubs       []string `json:",omitempty"`
329	ProductHiddenAPIStubsSystem []string `json:",omitempty"`
330	ProductHiddenAPIStubsTest   []string `json:",omitempty"`
331
332	ProductPublicSepolicyDirs  []string `json:",omitempty"`
333	ProductPrivateSepolicyDirs []string `json:",omitempty"`
334	ProductCompatibleProperty  *bool    `json:",omitempty"`
335
336	ProductVndkVersion *string `json:",omitempty"`
337
338	TargetFSConfigGen []string `json:",omitempty"`
339
340	MissingUsesLibraries []string `json:",omitempty"`
341
342	EnforceProductPartitionInterface *bool `json:",omitempty"`
343
344	InstallExtraFlattenedApexes *bool `json:",omitempty"`
345
346	BoardUsesRecoveryAsBoot *bool `json:",omitempty"`
347
348	BoardKernelBinaries []string `json:",omitempty"`
349}
350
351func boolPtr(v bool) *bool {
352	return &v
353}
354
355func intPtr(v int) *int {
356	return &v
357}
358
359func stringPtr(v string) *string {
360	return &v
361}
362
363func (v *productVariables) SetDefaultConfig() {
364	*v = productVariables{
365		BuildNumberFile: stringPtr("build_number.txt"),
366
367		Platform_version_name:             stringPtr("Q"),
368		Platform_sdk_version:              intPtr(28),
369		Platform_sdk_codename:             stringPtr("Q"),
370		Platform_sdk_final:                boolPtr(false),
371		Platform_version_active_codenames: []string{"Q"},
372		Platform_vndk_version:             stringPtr("Q"),
373
374		HostArch:                   stringPtr("x86_64"),
375		HostSecondaryArch:          stringPtr("x86"),
376		DeviceName:                 stringPtr("generic_arm64"),
377		DeviceArch:                 stringPtr("arm64"),
378		DeviceArchVariant:          stringPtr("armv8-a"),
379		DeviceCpuVariant:           stringPtr("generic"),
380		DeviceAbi:                  []string{"arm64-v8a"},
381		DeviceSecondaryArch:        stringPtr("arm"),
382		DeviceSecondaryArchVariant: stringPtr("armv8-a"),
383		DeviceSecondaryCpuVariant:  stringPtr("generic"),
384		DeviceSecondaryAbi:         []string{"armeabi-v7a", "armeabi"},
385
386		AAPTConfig:          []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
387		AAPTPreferredConfig: stringPtr("xhdpi"),
388		AAPTCharacteristics: stringPtr("nosdcard"),
389		AAPTPrebuiltDPI:     []string{"xhdpi", "xxhdpi"},
390
391		Malloc_not_svelte:            boolPtr(true),
392		Malloc_zero_contents:         boolPtr(false),
393		Malloc_pattern_fill_contents: boolPtr(false),
394		Safestack:                    boolPtr(false),
395	}
396
397	if runtime.GOOS == "linux" {
398		v.CrossHost = stringPtr("windows")
399		v.CrossHostArch = stringPtr("x86")
400		v.CrossHostSecondaryArch = stringPtr("x86_64")
401	}
402}
403
404func VariableMutator(mctx BottomUpMutatorContext) {
405	var module Module
406	var ok bool
407	if module, ok = mctx.Module().(Module); !ok {
408		return
409	}
410
411	// TODO: depend on config variable, create variants, propagate variants up tree
412	a := module.base()
413
414	if a.variableProperties == nil {
415		return
416	}
417
418	variableValues := reflect.ValueOf(a.variableProperties).Elem().FieldByName("Product_variables")
419
420	for i := 0; i < variableValues.NumField(); i++ {
421		variableValue := variableValues.Field(i)
422		name := variableValues.Type().Field(i).Name
423		property := "product_variables." + proptools.PropertyNameForField(name)
424
425		// Check that the variable was set for the product
426		val := reflect.ValueOf(mctx.Config().productVariables).FieldByName(name)
427		if !val.IsValid() || val.Kind() != reflect.Ptr || val.IsNil() {
428			continue
429		}
430
431		val = val.Elem()
432
433		// For bools, check that the value is true
434		if val.Kind() == reflect.Bool && val.Bool() == false {
435			continue
436		}
437
438		// Check if any properties were set for the module
439		if variableValue.IsZero() {
440			continue
441		}
442		a.setVariableProperties(mctx, property, variableValue, val.Interface())
443	}
444}
445
446func (m *ModuleBase) setVariableProperties(ctx BottomUpMutatorContext,
447	prefix string, productVariablePropertyValue reflect.Value, variableValue interface{}) {
448
449	printfIntoProperties(ctx, prefix, productVariablePropertyValue, variableValue)
450
451	err := proptools.AppendMatchingProperties(m.generalProperties,
452		productVariablePropertyValue.Addr().Interface(), nil)
453	if err != nil {
454		if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
455			ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
456		} else {
457			panic(err)
458		}
459	}
460}
461
462func printfIntoPropertiesError(ctx BottomUpMutatorContext, prefix string,
463	productVariablePropertyValue reflect.Value, i int, err error) {
464
465	field := productVariablePropertyValue.Type().Field(i).Name
466	property := prefix + "." + proptools.PropertyNameForField(field)
467	ctx.PropertyErrorf(property, "%s", err)
468}
469
470func printfIntoProperties(ctx BottomUpMutatorContext, prefix string,
471	productVariablePropertyValue reflect.Value, variableValue interface{}) {
472
473	for i := 0; i < productVariablePropertyValue.NumField(); i++ {
474		propertyValue := productVariablePropertyValue.Field(i)
475		kind := propertyValue.Kind()
476		if kind == reflect.Ptr {
477			if propertyValue.IsNil() {
478				continue
479			}
480			propertyValue = propertyValue.Elem()
481		}
482		switch propertyValue.Kind() {
483		case reflect.String:
484			err := printfIntoProperty(propertyValue, variableValue)
485			if err != nil {
486				printfIntoPropertiesError(ctx, prefix, productVariablePropertyValue, i, err)
487			}
488		case reflect.Slice:
489			for j := 0; j < propertyValue.Len(); j++ {
490				err := printfIntoProperty(propertyValue.Index(j), variableValue)
491				if err != nil {
492					printfIntoPropertiesError(ctx, prefix, productVariablePropertyValue, i, err)
493				}
494			}
495		case reflect.Bool:
496			// Nothing
497		case reflect.Struct:
498			printfIntoProperties(ctx, prefix, propertyValue, variableValue)
499		default:
500			panic(fmt.Errorf("unsupported field kind %q", propertyValue.Kind()))
501		}
502	}
503}
504
505func printfIntoProperty(propertyValue reflect.Value, variableValue interface{}) error {
506	s := propertyValue.String()
507
508	count := strings.Count(s, "%")
509	if count == 0 {
510		return nil
511	}
512
513	if count > 1 {
514		return fmt.Errorf("product variable properties only support a single '%%'")
515	}
516
517	if strings.Contains(s, "%d") {
518		switch v := variableValue.(type) {
519		case int:
520			// Nothing
521		case bool:
522			if v {
523				variableValue = 1
524			} else {
525				variableValue = 0
526			}
527		default:
528			return fmt.Errorf("unsupported type %T for %%d", variableValue)
529		}
530	} else if strings.Contains(s, "%s") {
531		switch variableValue.(type) {
532		case string:
533			// Nothing
534		default:
535			return fmt.Errorf("unsupported type %T for %%s", variableValue)
536		}
537	} else {
538		return fmt.Errorf("unsupported %% in product variable property")
539	}
540
541	propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, variableValue)))
542
543	return nil
544}
545
546var variablePropTypeMap OncePer
547
548// sliceToTypeArray takes a slice of property structs and returns a reflection created array containing the
549// reflect.Types of each property struct.  The result can be used as a key in a map.
550func sliceToTypeArray(s []interface{}) interface{} {
551	// Create an array using reflection whose length is the length of the input slice
552	ret := reflect.New(reflect.ArrayOf(len(s), reflect.TypeOf(reflect.TypeOf(0)))).Elem()
553	for i, e := range s {
554		ret.Index(i).Set(reflect.ValueOf(reflect.TypeOf(e)))
555	}
556	return ret.Interface()
557}
558
559func initProductVariableModule(m Module) {
560	base := m.base()
561
562	// Allow tests to override the default product variables
563	if base.variableProperties == nil {
564		base.variableProperties = defaultProductVariables
565	}
566	// Filter the product variables properties to the ones that exist on this module
567	base.variableProperties = createVariableProperties(m.GetProperties(), base.variableProperties)
568	if base.variableProperties != nil {
569		m.AddProperties(base.variableProperties)
570	}
571}
572
573// createVariableProperties takes the list of property structs for a module and returns a property struct that
574// contains the product variable properties that exist in the property structs, or nil if there are none.  It
575// caches the result.
576func createVariableProperties(moduleTypeProps []interface{}, productVariables interface{}) interface{} {
577	// Convert the moduleTypeProps to an array of reflect.Types that can be used as a key in the OncePer.
578	key := sliceToTypeArray(moduleTypeProps)
579
580	// Use the variablePropTypeMap OncePer to cache the result for each set of property struct types.
581	typ, _ := variablePropTypeMap.Once(NewCustomOnceKey(key), func() interface{} {
582		// Compute the filtered property struct type.
583		return createVariablePropertiesType(moduleTypeProps, productVariables)
584	}).(reflect.Type)
585
586	if typ == nil {
587		return nil
588	}
589
590	// Create a new pointer to a filtered property struct.
591	return reflect.New(typ).Interface()
592}
593
594// createVariablePropertiesType creates a new type that contains only the product variable properties that exist in
595// a list of property structs.
596func createVariablePropertiesType(moduleTypeProps []interface{}, productVariables interface{}) reflect.Type {
597	typ, _ := proptools.FilterPropertyStruct(reflect.TypeOf(productVariables),
598		func(field reflect.StructField, prefix string) (bool, reflect.StructField) {
599			// Filter function, returns true if the field should be in the resulting struct
600			if prefix == "" {
601				// Keep the top level Product_variables field
602				return true, field
603			}
604			_, rest := splitPrefix(prefix)
605			if rest == "" {
606				// Keep the 2nd level field (i.e. Product_variables.Eng)
607				return true, field
608			}
609
610			// Strip off the first 2 levels of the prefix
611			_, prefix = splitPrefix(rest)
612
613			for _, p := range moduleTypeProps {
614				if fieldExistsByNameRecursive(reflect.TypeOf(p).Elem(), prefix, field.Name) {
615					// Keep any fields that exist in one of the property structs
616					return true, field
617				}
618			}
619
620			return false, field
621		})
622	return typ
623}
624
625func splitPrefix(prefix string) (first, rest string) {
626	index := strings.IndexByte(prefix, '.')
627	if index == -1 {
628		return prefix, ""
629	}
630	return prefix[:index], prefix[index+1:]
631}
632
633func fieldExistsByNameRecursive(t reflect.Type, prefix, name string) bool {
634	if t.Kind() != reflect.Struct {
635		panic(fmt.Errorf("fieldExistsByNameRecursive can only be called on a reflect.Struct"))
636	}
637
638	if prefix != "" {
639		split := strings.SplitN(prefix, ".", 2)
640		firstPrefix := split[0]
641		rest := ""
642		if len(split) > 1 {
643			rest = split[1]
644		}
645		f, exists := t.FieldByName(firstPrefix)
646		if !exists {
647			return false
648		}
649		ft := f.Type
650		if ft.Kind() == reflect.Ptr {
651			ft = ft.Elem()
652		}
653		if ft.Kind() != reflect.Struct {
654			panic(fmt.Errorf("field %q in %q is not a struct", firstPrefix, t))
655		}
656		return fieldExistsByNameRecursive(ft, rest, name)
657	} else {
658		_, exists := t.FieldByName(name)
659		return exists
660	}
661}
662