xref: /aosp_15_r20/build/blueprint/proptools/unpack.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	"sort"
21*1fa6dee9SAndroid Build Coastguard Worker	"strconv"
22*1fa6dee9SAndroid Build Coastguard Worker	"strings"
23*1fa6dee9SAndroid Build Coastguard Worker	"text/scanner"
24*1fa6dee9SAndroid Build Coastguard Worker
25*1fa6dee9SAndroid Build Coastguard Worker	"github.com/google/blueprint/parser"
26*1fa6dee9SAndroid Build Coastguard Worker)
27*1fa6dee9SAndroid Build Coastguard Worker
28*1fa6dee9SAndroid Build Coastguard Workerconst maxUnpackErrors = 10
29*1fa6dee9SAndroid Build Coastguard Worker
30*1fa6dee9SAndroid Build Coastguard Workertype UnpackError struct {
31*1fa6dee9SAndroid Build Coastguard Worker	Err error
32*1fa6dee9SAndroid Build Coastguard Worker	Pos scanner.Position
33*1fa6dee9SAndroid Build Coastguard Worker}
34*1fa6dee9SAndroid Build Coastguard Worker
35*1fa6dee9SAndroid Build Coastguard Workerfunc (e *UnpackError) Error() string {
36*1fa6dee9SAndroid Build Coastguard Worker	return fmt.Sprintf("%s: %s", e.Pos, e.Err)
37*1fa6dee9SAndroid Build Coastguard Worker}
38*1fa6dee9SAndroid Build Coastguard Worker
39*1fa6dee9SAndroid Build Coastguard Worker// packedProperty helps to track properties usage (`used` will be true)
40*1fa6dee9SAndroid Build Coastguard Workertype packedProperty struct {
41*1fa6dee9SAndroid Build Coastguard Worker	property *parser.Property
42*1fa6dee9SAndroid Build Coastguard Worker	used     bool
43*1fa6dee9SAndroid Build Coastguard Worker}
44*1fa6dee9SAndroid Build Coastguard Worker
45*1fa6dee9SAndroid Build Coastguard Worker// unpackContext keeps compound names and their values in a map. It is initialized from
46*1fa6dee9SAndroid Build Coastguard Worker// parsed properties.
47*1fa6dee9SAndroid Build Coastguard Workertype unpackContext struct {
48*1fa6dee9SAndroid Build Coastguard Worker	propertyMap map[string]*packedProperty
49*1fa6dee9SAndroid Build Coastguard Worker	errs        []error
50*1fa6dee9SAndroid Build Coastguard Worker}
51*1fa6dee9SAndroid Build Coastguard Worker
52*1fa6dee9SAndroid Build Coastguard Worker// UnpackProperties populates the list of runtime values ("property structs") from the parsed properties.
53*1fa6dee9SAndroid Build Coastguard Worker// If a property a.b.c has a value, a field with the matching name in each runtime value is initialized
54*1fa6dee9SAndroid Build Coastguard Worker// from it. See PropertyNameForField for field and property name matching.
55*1fa6dee9SAndroid Build Coastguard Worker// For instance, if the input contains
56*1fa6dee9SAndroid Build Coastguard Worker//
57*1fa6dee9SAndroid Build Coastguard Worker//	{ foo: "abc", bar: {x: 1},}
58*1fa6dee9SAndroid Build Coastguard Worker//
59*1fa6dee9SAndroid Build Coastguard Worker// and a runtime value being has been declared as
60*1fa6dee9SAndroid Build Coastguard Worker//
61*1fa6dee9SAndroid Build Coastguard Worker//	var v struct { Foo string; Bar int }
62*1fa6dee9SAndroid Build Coastguard Worker//
63*1fa6dee9SAndroid Build Coastguard Worker// then v.Foo will be set to "abc" and v.Bar will be set to 1
64*1fa6dee9SAndroid Build Coastguard Worker// (cf. unpack_test.go for further examples)
65*1fa6dee9SAndroid Build Coastguard Worker//
66*1fa6dee9SAndroid Build Coastguard Worker// The type of a receiving field has to match the property type, i.e., a bool/int/string field
67*1fa6dee9SAndroid Build Coastguard Worker// can be set from a property with bool/int/string value, a struct can be set from a map (only the
68*1fa6dee9SAndroid Build Coastguard Worker// matching fields are set), and an slice can be set from a list.
69*1fa6dee9SAndroid Build Coastguard Worker// If a field of a runtime value has been already set prior to the UnpackProperties, the new value
70*1fa6dee9SAndroid Build Coastguard Worker// is appended to it (see somewhat inappropriately named ExtendBasicType).
71*1fa6dee9SAndroid Build Coastguard Worker// The same property can initialize fields in multiple runtime values. It is an error if any property
72*1fa6dee9SAndroid Build Coastguard Worker// value was not used to initialize at least one field.
73*1fa6dee9SAndroid Build Coastguard Workerfunc UnpackProperties(properties []*parser.Property, objects ...interface{}) (map[string]*parser.Property, []error) {
74*1fa6dee9SAndroid Build Coastguard Worker	var unpackContext unpackContext
75*1fa6dee9SAndroid Build Coastguard Worker	unpackContext.propertyMap = make(map[string]*packedProperty)
76*1fa6dee9SAndroid Build Coastguard Worker	if !unpackContext.buildPropertyMap("", properties) {
77*1fa6dee9SAndroid Build Coastguard Worker		return nil, unpackContext.errs
78*1fa6dee9SAndroid Build Coastguard Worker	}
79*1fa6dee9SAndroid Build Coastguard Worker
80*1fa6dee9SAndroid Build Coastguard Worker	for _, obj := range objects {
81*1fa6dee9SAndroid Build Coastguard Worker		valueObject := reflect.ValueOf(obj)
82*1fa6dee9SAndroid Build Coastguard Worker		if !isStructPtr(valueObject.Type()) {
83*1fa6dee9SAndroid Build Coastguard Worker			panic(fmt.Errorf("properties must be *struct, got %s",
84*1fa6dee9SAndroid Build Coastguard Worker				valueObject.Type()))
85*1fa6dee9SAndroid Build Coastguard Worker		}
86*1fa6dee9SAndroid Build Coastguard Worker		unpackContext.unpackToStruct("", valueObject.Elem())
87*1fa6dee9SAndroid Build Coastguard Worker		if len(unpackContext.errs) >= maxUnpackErrors {
88*1fa6dee9SAndroid Build Coastguard Worker			return nil, unpackContext.errs
89*1fa6dee9SAndroid Build Coastguard Worker		}
90*1fa6dee9SAndroid Build Coastguard Worker	}
91*1fa6dee9SAndroid Build Coastguard Worker
92*1fa6dee9SAndroid Build Coastguard Worker	// Gather property map, and collect any unused properties.
93*1fa6dee9SAndroid Build Coastguard Worker	// Avoid reporting subproperties of unused properties.
94*1fa6dee9SAndroid Build Coastguard Worker	result := make(map[string]*parser.Property)
95*1fa6dee9SAndroid Build Coastguard Worker	var unusedNames []string
96*1fa6dee9SAndroid Build Coastguard Worker	for name, v := range unpackContext.propertyMap {
97*1fa6dee9SAndroid Build Coastguard Worker		if v.used {
98*1fa6dee9SAndroid Build Coastguard Worker			result[name] = v.property
99*1fa6dee9SAndroid Build Coastguard Worker		} else {
100*1fa6dee9SAndroid Build Coastguard Worker			unusedNames = append(unusedNames, name)
101*1fa6dee9SAndroid Build Coastguard Worker		}
102*1fa6dee9SAndroid Build Coastguard Worker	}
103*1fa6dee9SAndroid Build Coastguard Worker	if len(unusedNames) == 0 && len(unpackContext.errs) == 0 {
104*1fa6dee9SAndroid Build Coastguard Worker		return result, nil
105*1fa6dee9SAndroid Build Coastguard Worker	}
106*1fa6dee9SAndroid Build Coastguard Worker	return nil, unpackContext.reportUnusedNames(unusedNames)
107*1fa6dee9SAndroid Build Coastguard Worker}
108*1fa6dee9SAndroid Build Coastguard Worker
109*1fa6dee9SAndroid Build Coastguard Workerfunc (ctx *unpackContext) reportUnusedNames(unusedNames []string) []error {
110*1fa6dee9SAndroid Build Coastguard Worker	sort.Strings(unusedNames)
111*1fa6dee9SAndroid Build Coastguard Worker	unusedNames = removeUnnecessaryUnusedNames(unusedNames)
112*1fa6dee9SAndroid Build Coastguard Worker	var lastReported string
113*1fa6dee9SAndroid Build Coastguard Worker	for _, name := range unusedNames {
114*1fa6dee9SAndroid Build Coastguard Worker		// if 'foo' has been reported, ignore 'foo\..*' and 'foo\[.*'
115*1fa6dee9SAndroid Build Coastguard Worker		if lastReported != "" {
116*1fa6dee9SAndroid Build Coastguard Worker			trimmed := strings.TrimPrefix(name, lastReported)
117*1fa6dee9SAndroid Build Coastguard Worker			if trimmed != name && (trimmed[0] == '.' || trimmed[0] == '[') {
118*1fa6dee9SAndroid Build Coastguard Worker				continue
119*1fa6dee9SAndroid Build Coastguard Worker			}
120*1fa6dee9SAndroid Build Coastguard Worker		}
121*1fa6dee9SAndroid Build Coastguard Worker		ctx.errs = append(ctx.errs, &UnpackError{
122*1fa6dee9SAndroid Build Coastguard Worker			fmt.Errorf("unrecognized property %q", name),
123*1fa6dee9SAndroid Build Coastguard Worker			ctx.propertyMap[name].property.ColonPos})
124*1fa6dee9SAndroid Build Coastguard Worker		lastReported = name
125*1fa6dee9SAndroid Build Coastguard Worker	}
126*1fa6dee9SAndroid Build Coastguard Worker	return ctx.errs
127*1fa6dee9SAndroid Build Coastguard Worker}
128*1fa6dee9SAndroid Build Coastguard Worker
129*1fa6dee9SAndroid Build Coastguard Worker// When property a.b.c is not used, (also there is no a.* or a.b.* used)
130*1fa6dee9SAndroid Build Coastguard Worker// "a", "a.b" and "a.b.c" are all in unusedNames.
131*1fa6dee9SAndroid Build Coastguard Worker// removeUnnecessaryUnusedNames only keeps the last "a.b.c" as the real unused
132*1fa6dee9SAndroid Build Coastguard Worker// name.
133*1fa6dee9SAndroid Build Coastguard Workerfunc removeUnnecessaryUnusedNames(names []string) []string {
134*1fa6dee9SAndroid Build Coastguard Worker	if len(names) == 0 {
135*1fa6dee9SAndroid Build Coastguard Worker		return names
136*1fa6dee9SAndroid Build Coastguard Worker	}
137*1fa6dee9SAndroid Build Coastguard Worker	var simplifiedNames []string
138*1fa6dee9SAndroid Build Coastguard Worker	for index, name := range names {
139*1fa6dee9SAndroid Build Coastguard Worker		if index == len(names)-1 || !strings.HasPrefix(names[index+1], name) {
140*1fa6dee9SAndroid Build Coastguard Worker			simplifiedNames = append(simplifiedNames, name)
141*1fa6dee9SAndroid Build Coastguard Worker		}
142*1fa6dee9SAndroid Build Coastguard Worker	}
143*1fa6dee9SAndroid Build Coastguard Worker	return simplifiedNames
144*1fa6dee9SAndroid Build Coastguard Worker}
145*1fa6dee9SAndroid Build Coastguard Worker
146*1fa6dee9SAndroid Build Coastguard Workerfunc (ctx *unpackContext) buildPropertyMap(prefix string, properties []*parser.Property) bool {
147*1fa6dee9SAndroid Build Coastguard Worker	nOldErrors := len(ctx.errs)
148*1fa6dee9SAndroid Build Coastguard Worker	for _, property := range properties {
149*1fa6dee9SAndroid Build Coastguard Worker		name := fieldPath(prefix, property.Name)
150*1fa6dee9SAndroid Build Coastguard Worker		if first, present := ctx.propertyMap[name]; present {
151*1fa6dee9SAndroid Build Coastguard Worker			ctx.addError(
152*1fa6dee9SAndroid Build Coastguard Worker				&UnpackError{fmt.Errorf("property %q already defined", name), property.ColonPos})
153*1fa6dee9SAndroid Build Coastguard Worker			if ctx.addError(
154*1fa6dee9SAndroid Build Coastguard Worker				&UnpackError{fmt.Errorf("<-- previous definition here"), first.property.ColonPos}) {
155*1fa6dee9SAndroid Build Coastguard Worker				return false
156*1fa6dee9SAndroid Build Coastguard Worker			}
157*1fa6dee9SAndroid Build Coastguard Worker			continue
158*1fa6dee9SAndroid Build Coastguard Worker		}
159*1fa6dee9SAndroid Build Coastguard Worker
160*1fa6dee9SAndroid Build Coastguard Worker		ctx.propertyMap[name] = &packedProperty{property, false}
161*1fa6dee9SAndroid Build Coastguard Worker		switch propValue := property.Value.(type) {
162*1fa6dee9SAndroid Build Coastguard Worker		case *parser.Map:
163*1fa6dee9SAndroid Build Coastguard Worker			ctx.buildPropertyMap(name, propValue.Properties)
164*1fa6dee9SAndroid Build Coastguard Worker		case *parser.List:
165*1fa6dee9SAndroid Build Coastguard Worker			// If it is a list, unroll it unless its elements are of primitive type
166*1fa6dee9SAndroid Build Coastguard Worker			// (no further mapping will be needed in that case, so we avoid cluttering
167*1fa6dee9SAndroid Build Coastguard Worker			// the map).
168*1fa6dee9SAndroid Build Coastguard Worker			if len(propValue.Values) == 0 {
169*1fa6dee9SAndroid Build Coastguard Worker				continue
170*1fa6dee9SAndroid Build Coastguard Worker			}
171*1fa6dee9SAndroid Build Coastguard Worker			if t := propValue.Values[0].Type(); t == parser.StringType || t == parser.Int64Type || t == parser.BoolType {
172*1fa6dee9SAndroid Build Coastguard Worker				continue
173*1fa6dee9SAndroid Build Coastguard Worker			}
174*1fa6dee9SAndroid Build Coastguard Worker
175*1fa6dee9SAndroid Build Coastguard Worker			itemProperties := make([]*parser.Property, len(propValue.Values))
176*1fa6dee9SAndroid Build Coastguard Worker			for i, expr := range propValue.Values {
177*1fa6dee9SAndroid Build Coastguard Worker				itemProperties[i] = &parser.Property{
178*1fa6dee9SAndroid Build Coastguard Worker					Name:     property.Name + "[" + strconv.Itoa(i) + "]",
179*1fa6dee9SAndroid Build Coastguard Worker					NamePos:  property.NamePos,
180*1fa6dee9SAndroid Build Coastguard Worker					ColonPos: property.ColonPos,
181*1fa6dee9SAndroid Build Coastguard Worker					Value:    expr,
182*1fa6dee9SAndroid Build Coastguard Worker				}
183*1fa6dee9SAndroid Build Coastguard Worker			}
184*1fa6dee9SAndroid Build Coastguard Worker			if !ctx.buildPropertyMap(prefix, itemProperties) {
185*1fa6dee9SAndroid Build Coastguard Worker				return false
186*1fa6dee9SAndroid Build Coastguard Worker			}
187*1fa6dee9SAndroid Build Coastguard Worker		}
188*1fa6dee9SAndroid Build Coastguard Worker	}
189*1fa6dee9SAndroid Build Coastguard Worker
190*1fa6dee9SAndroid Build Coastguard Worker	return len(ctx.errs) == nOldErrors
191*1fa6dee9SAndroid Build Coastguard Worker}
192*1fa6dee9SAndroid Build Coastguard Worker
193*1fa6dee9SAndroid Build Coastguard Workerfunc fieldPath(prefix, fieldName string) string {
194*1fa6dee9SAndroid Build Coastguard Worker	if prefix == "" {
195*1fa6dee9SAndroid Build Coastguard Worker		return fieldName
196*1fa6dee9SAndroid Build Coastguard Worker	}
197*1fa6dee9SAndroid Build Coastguard Worker	return prefix + "." + fieldName
198*1fa6dee9SAndroid Build Coastguard Worker}
199*1fa6dee9SAndroid Build Coastguard Worker
200*1fa6dee9SAndroid Build Coastguard Workerfunc (ctx *unpackContext) addError(e error) bool {
201*1fa6dee9SAndroid Build Coastguard Worker	ctx.errs = append(ctx.errs, e)
202*1fa6dee9SAndroid Build Coastguard Worker	return len(ctx.errs) < maxUnpackErrors
203*1fa6dee9SAndroid Build Coastguard Worker}
204*1fa6dee9SAndroid Build Coastguard Worker
205*1fa6dee9SAndroid Build Coastguard Workerfunc (ctx *unpackContext) unpackToStruct(namePrefix string, structValue reflect.Value) {
206*1fa6dee9SAndroid Build Coastguard Worker	structType := structValue.Type()
207*1fa6dee9SAndroid Build Coastguard Worker
208*1fa6dee9SAndroid Build Coastguard Worker	for i := 0; i < structValue.NumField(); i++ {
209*1fa6dee9SAndroid Build Coastguard Worker		fieldValue := structValue.Field(i)
210*1fa6dee9SAndroid Build Coastguard Worker		field := structType.Field(i)
211*1fa6dee9SAndroid Build Coastguard Worker
212*1fa6dee9SAndroid Build Coastguard Worker		// In Go 1.7, runtime-created structs are unexported, so it's not
213*1fa6dee9SAndroid Build Coastguard Worker		// possible to create an exported anonymous field with a generated
214*1fa6dee9SAndroid Build Coastguard Worker		// type. So workaround this by special-casing "BlueprintEmbed" to
215*1fa6dee9SAndroid Build Coastguard Worker		// behave like an anonymous field for structure unpacking.
216*1fa6dee9SAndroid Build Coastguard Worker		if field.Name == "BlueprintEmbed" {
217*1fa6dee9SAndroid Build Coastguard Worker			field.Name = ""
218*1fa6dee9SAndroid Build Coastguard Worker			field.Anonymous = true
219*1fa6dee9SAndroid Build Coastguard Worker		}
220*1fa6dee9SAndroid Build Coastguard Worker
221*1fa6dee9SAndroid Build Coastguard Worker		if field.PkgPath != "" {
222*1fa6dee9SAndroid Build Coastguard Worker			// This is an unexported field, so just skip it.
223*1fa6dee9SAndroid Build Coastguard Worker			continue
224*1fa6dee9SAndroid Build Coastguard Worker		}
225*1fa6dee9SAndroid Build Coastguard Worker
226*1fa6dee9SAndroid Build Coastguard Worker		propertyName := fieldPath(namePrefix, PropertyNameForField(field.Name))
227*1fa6dee9SAndroid Build Coastguard Worker
228*1fa6dee9SAndroid Build Coastguard Worker		if !fieldValue.CanSet() {
229*1fa6dee9SAndroid Build Coastguard Worker			panic(fmt.Errorf("field %s is not settable", propertyName))
230*1fa6dee9SAndroid Build Coastguard Worker		}
231*1fa6dee9SAndroid Build Coastguard Worker
232*1fa6dee9SAndroid Build Coastguard Worker		// Get the property value if it was specified.
233*1fa6dee9SAndroid Build Coastguard Worker		packedProperty, propertyIsSet := ctx.propertyMap[propertyName]
234*1fa6dee9SAndroid Build Coastguard Worker
235*1fa6dee9SAndroid Build Coastguard Worker		origFieldValue := fieldValue
236*1fa6dee9SAndroid Build Coastguard Worker
237*1fa6dee9SAndroid Build Coastguard Worker		// To make testing easier we validate the struct field's type regardless
238*1fa6dee9SAndroid Build Coastguard Worker		// of whether or not the property was specified in the parsed string.
239*1fa6dee9SAndroid Build Coastguard Worker		// TODO(ccross): we don't validate types inside nil struct pointers
240*1fa6dee9SAndroid Build Coastguard Worker		// Move type validation to a function that runs on each factory once
241*1fa6dee9SAndroid Build Coastguard Worker		switch kind := fieldValue.Kind(); kind {
242*1fa6dee9SAndroid Build Coastguard Worker		case reflect.Bool, reflect.String, reflect.Struct, reflect.Slice:
243*1fa6dee9SAndroid Build Coastguard Worker			// Do nothing
244*1fa6dee9SAndroid Build Coastguard Worker		case reflect.Interface:
245*1fa6dee9SAndroid Build Coastguard Worker			if fieldValue.IsNil() {
246*1fa6dee9SAndroid Build Coastguard Worker				panic(fmt.Errorf("field %s contains a nil interface", propertyName))
247*1fa6dee9SAndroid Build Coastguard Worker			}
248*1fa6dee9SAndroid Build Coastguard Worker			fieldValue = fieldValue.Elem()
249*1fa6dee9SAndroid Build Coastguard Worker			elemType := fieldValue.Type()
250*1fa6dee9SAndroid Build Coastguard Worker			if elemType.Kind() != reflect.Ptr {
251*1fa6dee9SAndroid Build Coastguard Worker				panic(fmt.Errorf("field %s contains a non-pointer interface", propertyName))
252*1fa6dee9SAndroid Build Coastguard Worker			}
253*1fa6dee9SAndroid Build Coastguard Worker			fallthrough
254*1fa6dee9SAndroid Build Coastguard Worker		case reflect.Ptr:
255*1fa6dee9SAndroid Build Coastguard Worker			switch ptrKind := fieldValue.Type().Elem().Kind(); ptrKind {
256*1fa6dee9SAndroid Build Coastguard Worker			case reflect.Struct:
257*1fa6dee9SAndroid Build Coastguard Worker				if fieldValue.IsNil() && (propertyIsSet || field.Anonymous) {
258*1fa6dee9SAndroid Build Coastguard Worker					// Instantiate nil struct pointers
259*1fa6dee9SAndroid Build Coastguard Worker					// Set into origFieldValue in case it was an interface, in which case
260*1fa6dee9SAndroid Build Coastguard Worker					// fieldValue points to the unsettable pointer inside the interface
261*1fa6dee9SAndroid Build Coastguard Worker					fieldValue = reflect.New(fieldValue.Type().Elem())
262*1fa6dee9SAndroid Build Coastguard Worker					origFieldValue.Set(fieldValue)
263*1fa6dee9SAndroid Build Coastguard Worker				}
264*1fa6dee9SAndroid Build Coastguard Worker				fieldValue = fieldValue.Elem()
265*1fa6dee9SAndroid Build Coastguard Worker			case reflect.Bool, reflect.Int64, reflect.String:
266*1fa6dee9SAndroid Build Coastguard Worker				// Nothing
267*1fa6dee9SAndroid Build Coastguard Worker			default:
268*1fa6dee9SAndroid Build Coastguard Worker				panic(fmt.Errorf("field %s contains a pointer to %s", propertyName, ptrKind))
269*1fa6dee9SAndroid Build Coastguard Worker			}
270*1fa6dee9SAndroid Build Coastguard Worker
271*1fa6dee9SAndroid Build Coastguard Worker		case reflect.Int, reflect.Uint:
272*1fa6dee9SAndroid Build Coastguard Worker			if !HasTag(field, "blueprint", "mutated") {
273*1fa6dee9SAndroid Build Coastguard Worker				panic(fmt.Errorf(`int field %s must be tagged blueprint:"mutated"`, propertyName))
274*1fa6dee9SAndroid Build Coastguard Worker			}
275*1fa6dee9SAndroid Build Coastguard Worker
276*1fa6dee9SAndroid Build Coastguard Worker		default:
277*1fa6dee9SAndroid Build Coastguard Worker			panic(fmt.Errorf("unsupported kind for field %s: %s", propertyName, kind))
278*1fa6dee9SAndroid Build Coastguard Worker		}
279*1fa6dee9SAndroid Build Coastguard Worker
280*1fa6dee9SAndroid Build Coastguard Worker		if field.Anonymous && isStruct(fieldValue.Type()) {
281*1fa6dee9SAndroid Build Coastguard Worker			ctx.unpackToStruct(namePrefix, fieldValue)
282*1fa6dee9SAndroid Build Coastguard Worker			continue
283*1fa6dee9SAndroid Build Coastguard Worker		}
284*1fa6dee9SAndroid Build Coastguard Worker
285*1fa6dee9SAndroid Build Coastguard Worker		if !propertyIsSet {
286*1fa6dee9SAndroid Build Coastguard Worker			// This property wasn't specified.
287*1fa6dee9SAndroid Build Coastguard Worker			continue
288*1fa6dee9SAndroid Build Coastguard Worker		}
289*1fa6dee9SAndroid Build Coastguard Worker
290*1fa6dee9SAndroid Build Coastguard Worker		packedProperty.used = true
291*1fa6dee9SAndroid Build Coastguard Worker		property := packedProperty.property
292*1fa6dee9SAndroid Build Coastguard Worker
293*1fa6dee9SAndroid Build Coastguard Worker		if HasTag(field, "blueprint", "mutated") {
294*1fa6dee9SAndroid Build Coastguard Worker			if !ctx.addError(
295*1fa6dee9SAndroid Build Coastguard Worker				&UnpackError{
296*1fa6dee9SAndroid Build Coastguard Worker					fmt.Errorf("mutated field %s cannot be set in a Blueprint file", propertyName),
297*1fa6dee9SAndroid Build Coastguard Worker					property.ColonPos,
298*1fa6dee9SAndroid Build Coastguard Worker				}) {
299*1fa6dee9SAndroid Build Coastguard Worker				return
300*1fa6dee9SAndroid Build Coastguard Worker			}
301*1fa6dee9SAndroid Build Coastguard Worker			continue
302*1fa6dee9SAndroid Build Coastguard Worker		}
303*1fa6dee9SAndroid Build Coastguard Worker
304*1fa6dee9SAndroid Build Coastguard Worker		if isConfigurable(fieldValue.Type()) {
305*1fa6dee9SAndroid Build Coastguard Worker			// configurableType is the reflect.Type representation of a Configurable[whatever],
306*1fa6dee9SAndroid Build Coastguard Worker			// while configuredType is the reflect.Type of the "whatever".
307*1fa6dee9SAndroid Build Coastguard Worker			configurableType := fieldValue.Type()
308*1fa6dee9SAndroid Build Coastguard Worker			configuredType := fieldValue.Interface().(configurableReflection).configuredType()
309*1fa6dee9SAndroid Build Coastguard Worker			if unpackedValue, ok := ctx.unpackToConfigurable(propertyName, property, configurableType, configuredType); ok {
310*1fa6dee9SAndroid Build Coastguard Worker				ExtendBasicType(fieldValue, unpackedValue.Elem(), Append)
311*1fa6dee9SAndroid Build Coastguard Worker			}
312*1fa6dee9SAndroid Build Coastguard Worker			if len(ctx.errs) >= maxUnpackErrors {
313*1fa6dee9SAndroid Build Coastguard Worker				return
314*1fa6dee9SAndroid Build Coastguard Worker			}
315*1fa6dee9SAndroid Build Coastguard Worker		} else if isStruct(fieldValue.Type()) {
316*1fa6dee9SAndroid Build Coastguard Worker			if property.Value.Type() != parser.MapType {
317*1fa6dee9SAndroid Build Coastguard Worker				ctx.addError(&UnpackError{
318*1fa6dee9SAndroid Build Coastguard Worker					fmt.Errorf("can't assign %s value to map property %q",
319*1fa6dee9SAndroid Build Coastguard Worker						property.Value.Type(), property.Name),
320*1fa6dee9SAndroid Build Coastguard Worker					property.Value.Pos(),
321*1fa6dee9SAndroid Build Coastguard Worker				})
322*1fa6dee9SAndroid Build Coastguard Worker				continue
323*1fa6dee9SAndroid Build Coastguard Worker			}
324*1fa6dee9SAndroid Build Coastguard Worker			ctx.unpackToStruct(propertyName, fieldValue)
325*1fa6dee9SAndroid Build Coastguard Worker			if len(ctx.errs) >= maxUnpackErrors {
326*1fa6dee9SAndroid Build Coastguard Worker				return
327*1fa6dee9SAndroid Build Coastguard Worker			}
328*1fa6dee9SAndroid Build Coastguard Worker		} else if isSlice(fieldValue.Type()) {
329*1fa6dee9SAndroid Build Coastguard Worker			if unpackedValue, ok := ctx.unpackToSlice(propertyName, property, fieldValue.Type()); ok {
330*1fa6dee9SAndroid Build Coastguard Worker				ExtendBasicType(fieldValue, unpackedValue, Append)
331*1fa6dee9SAndroid Build Coastguard Worker			}
332*1fa6dee9SAndroid Build Coastguard Worker			if len(ctx.errs) >= maxUnpackErrors {
333*1fa6dee9SAndroid Build Coastguard Worker				return
334*1fa6dee9SAndroid Build Coastguard Worker			}
335*1fa6dee9SAndroid Build Coastguard Worker		} else {
336*1fa6dee9SAndroid Build Coastguard Worker			unpackedValue, err := propertyToValue(fieldValue.Type(), property)
337*1fa6dee9SAndroid Build Coastguard Worker			if err != nil && !ctx.addError(err) {
338*1fa6dee9SAndroid Build Coastguard Worker				return
339*1fa6dee9SAndroid Build Coastguard Worker			}
340*1fa6dee9SAndroid Build Coastguard Worker			ExtendBasicType(fieldValue, unpackedValue, Append)
341*1fa6dee9SAndroid Build Coastguard Worker		}
342*1fa6dee9SAndroid Build Coastguard Worker	}
343*1fa6dee9SAndroid Build Coastguard Worker}
344*1fa6dee9SAndroid Build Coastguard Worker
345*1fa6dee9SAndroid Build Coastguard Worker// Converts the given property to a pointer to a configurable struct
346*1fa6dee9SAndroid Build Coastguard Workerfunc (ctx *unpackContext) unpackToConfigurable(propertyName string, property *parser.Property, configurableType, configuredType reflect.Type) (reflect.Value, bool) {
347*1fa6dee9SAndroid Build Coastguard Worker	switch v := property.Value.(type) {
348*1fa6dee9SAndroid Build Coastguard Worker	case *parser.String:
349*1fa6dee9SAndroid Build Coastguard Worker		if configuredType.Kind() != reflect.String {
350*1fa6dee9SAndroid Build Coastguard Worker			ctx.addError(&UnpackError{
351*1fa6dee9SAndroid Build Coastguard Worker				fmt.Errorf("can't assign string value to configurable %s property %q",
352*1fa6dee9SAndroid Build Coastguard Worker					configuredType.String(), property.Name),
353*1fa6dee9SAndroid Build Coastguard Worker				property.Value.Pos(),
354*1fa6dee9SAndroid Build Coastguard Worker			})
355*1fa6dee9SAndroid Build Coastguard Worker			return reflect.New(configurableType), false
356*1fa6dee9SAndroid Build Coastguard Worker		}
357*1fa6dee9SAndroid Build Coastguard Worker		var postProcessors [][]postProcessor[string]
358*1fa6dee9SAndroid Build Coastguard Worker		result := Configurable[string]{
359*1fa6dee9SAndroid Build Coastguard Worker			propertyName: property.Name,
360*1fa6dee9SAndroid Build Coastguard Worker			inner: &configurableInner[string]{
361*1fa6dee9SAndroid Build Coastguard Worker				single: singleConfigurable[string]{
362*1fa6dee9SAndroid Build Coastguard Worker					cases: []ConfigurableCase[string]{{
363*1fa6dee9SAndroid Build Coastguard Worker						value: v,
364*1fa6dee9SAndroid Build Coastguard Worker					}},
365*1fa6dee9SAndroid Build Coastguard Worker				},
366*1fa6dee9SAndroid Build Coastguard Worker			},
367*1fa6dee9SAndroid Build Coastguard Worker			postProcessors: &postProcessors,
368*1fa6dee9SAndroid Build Coastguard Worker		}
369*1fa6dee9SAndroid Build Coastguard Worker		return reflect.ValueOf(&result), true
370*1fa6dee9SAndroid Build Coastguard Worker	case *parser.Bool:
371*1fa6dee9SAndroid Build Coastguard Worker		if configuredType.Kind() != reflect.Bool {
372*1fa6dee9SAndroid Build Coastguard Worker			ctx.addError(&UnpackError{
373*1fa6dee9SAndroid Build Coastguard Worker				fmt.Errorf("can't assign bool value to configurable %s property %q",
374*1fa6dee9SAndroid Build Coastguard Worker					configuredType.String(), property.Name),
375*1fa6dee9SAndroid Build Coastguard Worker				property.Value.Pos(),
376*1fa6dee9SAndroid Build Coastguard Worker			})
377*1fa6dee9SAndroid Build Coastguard Worker			return reflect.New(configurableType), false
378*1fa6dee9SAndroid Build Coastguard Worker		}
379*1fa6dee9SAndroid Build Coastguard Worker		var postProcessors [][]postProcessor[bool]
380*1fa6dee9SAndroid Build Coastguard Worker		result := Configurable[bool]{
381*1fa6dee9SAndroid Build Coastguard Worker			propertyName: property.Name,
382*1fa6dee9SAndroid Build Coastguard Worker			inner: &configurableInner[bool]{
383*1fa6dee9SAndroid Build Coastguard Worker				single: singleConfigurable[bool]{
384*1fa6dee9SAndroid Build Coastguard Worker					cases: []ConfigurableCase[bool]{{
385*1fa6dee9SAndroid Build Coastguard Worker						value: v,
386*1fa6dee9SAndroid Build Coastguard Worker					}},
387*1fa6dee9SAndroid Build Coastguard Worker				},
388*1fa6dee9SAndroid Build Coastguard Worker			},
389*1fa6dee9SAndroid Build Coastguard Worker			postProcessors: &postProcessors,
390*1fa6dee9SAndroid Build Coastguard Worker		}
391*1fa6dee9SAndroid Build Coastguard Worker		return reflect.ValueOf(&result), true
392*1fa6dee9SAndroid Build Coastguard Worker	case *parser.List:
393*1fa6dee9SAndroid Build Coastguard Worker		if configuredType.Kind() != reflect.Slice {
394*1fa6dee9SAndroid Build Coastguard Worker			ctx.addError(&UnpackError{
395*1fa6dee9SAndroid Build Coastguard Worker				fmt.Errorf("can't assign list value to configurable %s property %q",
396*1fa6dee9SAndroid Build Coastguard Worker					configuredType.String(), property.Name),
397*1fa6dee9SAndroid Build Coastguard Worker				property.Value.Pos(),
398*1fa6dee9SAndroid Build Coastguard Worker			})
399*1fa6dee9SAndroid Build Coastguard Worker			return reflect.New(configurableType), false
400*1fa6dee9SAndroid Build Coastguard Worker		}
401*1fa6dee9SAndroid Build Coastguard Worker		switch configuredType.Elem().Kind() {
402*1fa6dee9SAndroid Build Coastguard Worker		case reflect.String:
403*1fa6dee9SAndroid Build Coastguard Worker			var value []string
404*1fa6dee9SAndroid Build Coastguard Worker			if v.Values != nil {
405*1fa6dee9SAndroid Build Coastguard Worker				value = make([]string, len(v.Values))
406*1fa6dee9SAndroid Build Coastguard Worker				itemProperty := &parser.Property{NamePos: property.NamePos, ColonPos: property.ColonPos}
407*1fa6dee9SAndroid Build Coastguard Worker				for i, expr := range v.Values {
408*1fa6dee9SAndroid Build Coastguard Worker					itemProperty.Name = propertyName + "[" + strconv.Itoa(i) + "]"
409*1fa6dee9SAndroid Build Coastguard Worker					itemProperty.Value = expr
410*1fa6dee9SAndroid Build Coastguard Worker					exprUnpacked, err := propertyToValue(configuredType.Elem(), itemProperty)
411*1fa6dee9SAndroid Build Coastguard Worker					if err != nil {
412*1fa6dee9SAndroid Build Coastguard Worker						ctx.addError(err)
413*1fa6dee9SAndroid Build Coastguard Worker						return reflect.ValueOf(Configurable[[]string]{}), false
414*1fa6dee9SAndroid Build Coastguard Worker					}
415*1fa6dee9SAndroid Build Coastguard Worker					value[i] = exprUnpacked.Interface().(string)
416*1fa6dee9SAndroid Build Coastguard Worker				}
417*1fa6dee9SAndroid Build Coastguard Worker			}
418*1fa6dee9SAndroid Build Coastguard Worker			var postProcessors [][]postProcessor[[]string]
419*1fa6dee9SAndroid Build Coastguard Worker			result := Configurable[[]string]{
420*1fa6dee9SAndroid Build Coastguard Worker				propertyName: property.Name,
421*1fa6dee9SAndroid Build Coastguard Worker				inner: &configurableInner[[]string]{
422*1fa6dee9SAndroid Build Coastguard Worker					single: singleConfigurable[[]string]{
423*1fa6dee9SAndroid Build Coastguard Worker						cases: []ConfigurableCase[[]string]{{
424*1fa6dee9SAndroid Build Coastguard Worker							value: v,
425*1fa6dee9SAndroid Build Coastguard Worker						}},
426*1fa6dee9SAndroid Build Coastguard Worker					},
427*1fa6dee9SAndroid Build Coastguard Worker				},
428*1fa6dee9SAndroid Build Coastguard Worker				postProcessors: &postProcessors,
429*1fa6dee9SAndroid Build Coastguard Worker			}
430*1fa6dee9SAndroid Build Coastguard Worker			return reflect.ValueOf(&result), true
431*1fa6dee9SAndroid Build Coastguard Worker		default:
432*1fa6dee9SAndroid Build Coastguard Worker			panic("This should be unreachable because ConfigurableElements only accepts slices of strings")
433*1fa6dee9SAndroid Build Coastguard Worker		}
434*1fa6dee9SAndroid Build Coastguard Worker	case *parser.Select:
435*1fa6dee9SAndroid Build Coastguard Worker		resultPtr := reflect.New(configurableType)
436*1fa6dee9SAndroid Build Coastguard Worker		result := resultPtr.Elem()
437*1fa6dee9SAndroid Build Coastguard Worker		conditions := make([]ConfigurableCondition, len(v.Conditions))
438*1fa6dee9SAndroid Build Coastguard Worker		for i, cond := range v.Conditions {
439*1fa6dee9SAndroid Build Coastguard Worker			args := make([]string, len(cond.Args))
440*1fa6dee9SAndroid Build Coastguard Worker			for j, arg := range cond.Args {
441*1fa6dee9SAndroid Build Coastguard Worker				args[j] = arg.Value
442*1fa6dee9SAndroid Build Coastguard Worker			}
443*1fa6dee9SAndroid Build Coastguard Worker			conditions[i] = ConfigurableCondition{
444*1fa6dee9SAndroid Build Coastguard Worker				functionName: cond.FunctionName,
445*1fa6dee9SAndroid Build Coastguard Worker				args:         args,
446*1fa6dee9SAndroid Build Coastguard Worker			}
447*1fa6dee9SAndroid Build Coastguard Worker		}
448*1fa6dee9SAndroid Build Coastguard Worker
449*1fa6dee9SAndroid Build Coastguard Worker		configurableCaseType := configurableCaseType(configuredType)
450*1fa6dee9SAndroid Build Coastguard Worker		cases := reflect.MakeSlice(reflect.SliceOf(configurableCaseType), 0, len(v.Cases))
451*1fa6dee9SAndroid Build Coastguard Worker		for _, c := range v.Cases {
452*1fa6dee9SAndroid Build Coastguard Worker			patterns := make([]ConfigurablePattern, len(c.Patterns))
453*1fa6dee9SAndroid Build Coastguard Worker			for i, pat := range c.Patterns {
454*1fa6dee9SAndroid Build Coastguard Worker				switch pat := pat.Value.(type) {
455*1fa6dee9SAndroid Build Coastguard Worker				case *parser.String:
456*1fa6dee9SAndroid Build Coastguard Worker					if pat.Value == "__soong_conditions_default__" {
457*1fa6dee9SAndroid Build Coastguard Worker						patterns[i].typ = configurablePatternTypeDefault
458*1fa6dee9SAndroid Build Coastguard Worker					} else if pat.Value == "__soong_conditions_any__" {
459*1fa6dee9SAndroid Build Coastguard Worker						patterns[i].typ = configurablePatternTypeAny
460*1fa6dee9SAndroid Build Coastguard Worker					} else {
461*1fa6dee9SAndroid Build Coastguard Worker						patterns[i].typ = configurablePatternTypeString
462*1fa6dee9SAndroid Build Coastguard Worker						patterns[i].stringValue = pat.Value
463*1fa6dee9SAndroid Build Coastguard Worker					}
464*1fa6dee9SAndroid Build Coastguard Worker				case *parser.Bool:
465*1fa6dee9SAndroid Build Coastguard Worker					patterns[i].typ = configurablePatternTypeBool
466*1fa6dee9SAndroid Build Coastguard Worker					patterns[i].boolValue = pat.Value
467*1fa6dee9SAndroid Build Coastguard Worker				default:
468*1fa6dee9SAndroid Build Coastguard Worker					panic("unimplemented")
469*1fa6dee9SAndroid Build Coastguard Worker				}
470*1fa6dee9SAndroid Build Coastguard Worker				patterns[i].binding = pat.Binding.Name
471*1fa6dee9SAndroid Build Coastguard Worker			}
472*1fa6dee9SAndroid Build Coastguard Worker
473*1fa6dee9SAndroid Build Coastguard Worker			case_ := reflect.New(configurableCaseType)
474*1fa6dee9SAndroid Build Coastguard Worker			case_.Interface().(configurableCaseReflection).initialize(patterns, c.Value)
475*1fa6dee9SAndroid Build Coastguard Worker			cases = reflect.Append(cases, case_.Elem())
476*1fa6dee9SAndroid Build Coastguard Worker		}
477*1fa6dee9SAndroid Build Coastguard Worker		resultPtr.Interface().(configurablePtrReflection).initialize(
478*1fa6dee9SAndroid Build Coastguard Worker			v.Scope,
479*1fa6dee9SAndroid Build Coastguard Worker			property.Name,
480*1fa6dee9SAndroid Build Coastguard Worker			conditions,
481*1fa6dee9SAndroid Build Coastguard Worker			cases.Interface(),
482*1fa6dee9SAndroid Build Coastguard Worker		)
483*1fa6dee9SAndroid Build Coastguard Worker		if v.Append != nil {
484*1fa6dee9SAndroid Build Coastguard Worker			p := &parser.Property{
485*1fa6dee9SAndroid Build Coastguard Worker				Name:    property.Name,
486*1fa6dee9SAndroid Build Coastguard Worker				NamePos: property.NamePos,
487*1fa6dee9SAndroid Build Coastguard Worker				Value:   v.Append,
488*1fa6dee9SAndroid Build Coastguard Worker			}
489*1fa6dee9SAndroid Build Coastguard Worker			val, ok := ctx.unpackToConfigurable(propertyName, p, configurableType, configuredType)
490*1fa6dee9SAndroid Build Coastguard Worker			if !ok {
491*1fa6dee9SAndroid Build Coastguard Worker				return reflect.New(configurableType), false
492*1fa6dee9SAndroid Build Coastguard Worker			}
493*1fa6dee9SAndroid Build Coastguard Worker			result.Interface().(configurableReflection).setAppend(val.Elem().Interface(), false, false)
494*1fa6dee9SAndroid Build Coastguard Worker		}
495*1fa6dee9SAndroid Build Coastguard Worker		return resultPtr, true
496*1fa6dee9SAndroid Build Coastguard Worker	default:
497*1fa6dee9SAndroid Build Coastguard Worker		ctx.addError(&UnpackError{
498*1fa6dee9SAndroid Build Coastguard Worker			fmt.Errorf("can't assign %s value to configurable %s property %q",
499*1fa6dee9SAndroid Build Coastguard Worker				property.Value.Type(), configuredType.String(), property.Name),
500*1fa6dee9SAndroid Build Coastguard Worker			property.Value.Pos(),
501*1fa6dee9SAndroid Build Coastguard Worker		})
502*1fa6dee9SAndroid Build Coastguard Worker		return reflect.New(configurableType), false
503*1fa6dee9SAndroid Build Coastguard Worker	}
504*1fa6dee9SAndroid Build Coastguard Worker}
505*1fa6dee9SAndroid Build Coastguard Worker
506*1fa6dee9SAndroid Build Coastguard Worker// If the given property is a select, returns an error saying that you can't assign a select to
507*1fa6dee9SAndroid Build Coastguard Worker// a non-configurable property. Otherwise returns nil.
508*1fa6dee9SAndroid Build Coastguard Workerfunc selectOnNonConfigurablePropertyError(property *parser.Property) error {
509*1fa6dee9SAndroid Build Coastguard Worker	if _, ok := property.Value.(*parser.Select); !ok {
510*1fa6dee9SAndroid Build Coastguard Worker		return nil
511*1fa6dee9SAndroid Build Coastguard Worker	}
512*1fa6dee9SAndroid Build Coastguard Worker
513*1fa6dee9SAndroid Build Coastguard Worker	return &UnpackError{
514*1fa6dee9SAndroid Build Coastguard Worker		fmt.Errorf("can't assign select statement to non-configurable property %q. This requires a small soong change to enable in most cases, please file a go/soong-bug if you'd like to use a select statement here",
515*1fa6dee9SAndroid Build Coastguard Worker			property.Name),
516*1fa6dee9SAndroid Build Coastguard Worker		property.Value.Pos(),
517*1fa6dee9SAndroid Build Coastguard Worker	}
518*1fa6dee9SAndroid Build Coastguard Worker}
519*1fa6dee9SAndroid Build Coastguard Worker
520*1fa6dee9SAndroid Build Coastguard Worker// unpackSlice creates a value of a given slice or pointer to slice type from the property,
521*1fa6dee9SAndroid Build Coastguard Worker// which should be a list
522*1fa6dee9SAndroid Build Coastguard Workerfunc (ctx *unpackContext) unpackToSlice(
523*1fa6dee9SAndroid Build Coastguard Worker	sliceName string, property *parser.Property, sliceType reflect.Type) (reflect.Value, bool) {
524*1fa6dee9SAndroid Build Coastguard Worker	if sliceType.Kind() == reflect.Pointer {
525*1fa6dee9SAndroid Build Coastguard Worker		sliceType = sliceType.Elem()
526*1fa6dee9SAndroid Build Coastguard Worker		result := reflect.New(sliceType)
527*1fa6dee9SAndroid Build Coastguard Worker		slice, ok := ctx.unpackToSliceInner(sliceName, property, sliceType)
528*1fa6dee9SAndroid Build Coastguard Worker		if !ok {
529*1fa6dee9SAndroid Build Coastguard Worker			return result, ok
530*1fa6dee9SAndroid Build Coastguard Worker		}
531*1fa6dee9SAndroid Build Coastguard Worker		result.Elem().Set(slice)
532*1fa6dee9SAndroid Build Coastguard Worker		return result, true
533*1fa6dee9SAndroid Build Coastguard Worker	}
534*1fa6dee9SAndroid Build Coastguard Worker	return ctx.unpackToSliceInner(sliceName, property, sliceType)
535*1fa6dee9SAndroid Build Coastguard Worker}
536*1fa6dee9SAndroid Build Coastguard Worker
537*1fa6dee9SAndroid Build Coastguard Worker// unpackToSliceInner creates a value of a given slice type from the property,
538*1fa6dee9SAndroid Build Coastguard Worker// which should be a list. It doesn't support pointers to slice types like unpackToSlice
539*1fa6dee9SAndroid Build Coastguard Worker// does.
540*1fa6dee9SAndroid Build Coastguard Workerfunc (ctx *unpackContext) unpackToSliceInner(
541*1fa6dee9SAndroid Build Coastguard Worker	sliceName string, property *parser.Property, sliceType reflect.Type) (reflect.Value, bool) {
542*1fa6dee9SAndroid Build Coastguard Worker	propValueAsList, ok := property.Value.(*parser.List)
543*1fa6dee9SAndroid Build Coastguard Worker	if !ok {
544*1fa6dee9SAndroid Build Coastguard Worker		if err := selectOnNonConfigurablePropertyError(property); err != nil {
545*1fa6dee9SAndroid Build Coastguard Worker			ctx.addError(err)
546*1fa6dee9SAndroid Build Coastguard Worker		} else {
547*1fa6dee9SAndroid Build Coastguard Worker			ctx.addError(&UnpackError{
548*1fa6dee9SAndroid Build Coastguard Worker				fmt.Errorf("can't assign %s value to list property %q",
549*1fa6dee9SAndroid Build Coastguard Worker					property.Value.Type(), property.Name),
550*1fa6dee9SAndroid Build Coastguard Worker				property.Value.Pos(),
551*1fa6dee9SAndroid Build Coastguard Worker			})
552*1fa6dee9SAndroid Build Coastguard Worker		}
553*1fa6dee9SAndroid Build Coastguard Worker		return reflect.MakeSlice(sliceType, 0, 0), false
554*1fa6dee9SAndroid Build Coastguard Worker	}
555*1fa6dee9SAndroid Build Coastguard Worker	exprs := propValueAsList.Values
556*1fa6dee9SAndroid Build Coastguard Worker	value := reflect.MakeSlice(sliceType, 0, len(exprs))
557*1fa6dee9SAndroid Build Coastguard Worker	if len(exprs) == 0 {
558*1fa6dee9SAndroid Build Coastguard Worker		return value, true
559*1fa6dee9SAndroid Build Coastguard Worker	}
560*1fa6dee9SAndroid Build Coastguard Worker
561*1fa6dee9SAndroid Build Coastguard Worker	// The function to construct an item value depends on the type of list elements.
562*1fa6dee9SAndroid Build Coastguard Worker	getItemFunc := func(property *parser.Property, t reflect.Type) (reflect.Value, bool) {
563*1fa6dee9SAndroid Build Coastguard Worker		switch property.Value.(type) {
564*1fa6dee9SAndroid Build Coastguard Worker		case *parser.Bool, *parser.String, *parser.Int64:
565*1fa6dee9SAndroid Build Coastguard Worker			value, err := propertyToValue(t, property)
566*1fa6dee9SAndroid Build Coastguard Worker			if err != nil {
567*1fa6dee9SAndroid Build Coastguard Worker				ctx.addError(err)
568*1fa6dee9SAndroid Build Coastguard Worker				return value, false
569*1fa6dee9SAndroid Build Coastguard Worker			}
570*1fa6dee9SAndroid Build Coastguard Worker			return value, true
571*1fa6dee9SAndroid Build Coastguard Worker		case *parser.List:
572*1fa6dee9SAndroid Build Coastguard Worker			return ctx.unpackToSlice(property.Name, property, t)
573*1fa6dee9SAndroid Build Coastguard Worker		case *parser.Map:
574*1fa6dee9SAndroid Build Coastguard Worker			itemValue := reflect.New(t).Elem()
575*1fa6dee9SAndroid Build Coastguard Worker			ctx.unpackToStruct(property.Name, itemValue)
576*1fa6dee9SAndroid Build Coastguard Worker			return itemValue, true
577*1fa6dee9SAndroid Build Coastguard Worker		default:
578*1fa6dee9SAndroid Build Coastguard Worker			panic(fmt.Errorf("bizarre property expression type: %v, %#v", property.Value.Type(), property.Value))
579*1fa6dee9SAndroid Build Coastguard Worker		}
580*1fa6dee9SAndroid Build Coastguard Worker	}
581*1fa6dee9SAndroid Build Coastguard Worker
582*1fa6dee9SAndroid Build Coastguard Worker	itemProperty := &parser.Property{NamePos: property.NamePos, ColonPos: property.ColonPos}
583*1fa6dee9SAndroid Build Coastguard Worker	elemType := sliceType.Elem()
584*1fa6dee9SAndroid Build Coastguard Worker	isPtr := elemType.Kind() == reflect.Ptr
585*1fa6dee9SAndroid Build Coastguard Worker
586*1fa6dee9SAndroid Build Coastguard Worker	for i, expr := range exprs {
587*1fa6dee9SAndroid Build Coastguard Worker		itemProperty.Name = sliceName + "[" + strconv.Itoa(i) + "]"
588*1fa6dee9SAndroid Build Coastguard Worker		itemProperty.Value = expr
589*1fa6dee9SAndroid Build Coastguard Worker		if packedProperty, ok := ctx.propertyMap[itemProperty.Name]; ok {
590*1fa6dee9SAndroid Build Coastguard Worker			packedProperty.used = true
591*1fa6dee9SAndroid Build Coastguard Worker		}
592*1fa6dee9SAndroid Build Coastguard Worker		if isPtr {
593*1fa6dee9SAndroid Build Coastguard Worker			if itemValue, ok := getItemFunc(itemProperty, elemType.Elem()); ok {
594*1fa6dee9SAndroid Build Coastguard Worker				ptrValue := reflect.New(itemValue.Type())
595*1fa6dee9SAndroid Build Coastguard Worker				ptrValue.Elem().Set(itemValue)
596*1fa6dee9SAndroid Build Coastguard Worker				value = reflect.Append(value, ptrValue)
597*1fa6dee9SAndroid Build Coastguard Worker			}
598*1fa6dee9SAndroid Build Coastguard Worker		} else {
599*1fa6dee9SAndroid Build Coastguard Worker			if itemValue, ok := getItemFunc(itemProperty, elemType); ok {
600*1fa6dee9SAndroid Build Coastguard Worker				value = reflect.Append(value, itemValue)
601*1fa6dee9SAndroid Build Coastguard Worker			}
602*1fa6dee9SAndroid Build Coastguard Worker		}
603*1fa6dee9SAndroid Build Coastguard Worker	}
604*1fa6dee9SAndroid Build Coastguard Worker	return value, true
605*1fa6dee9SAndroid Build Coastguard Worker}
606*1fa6dee9SAndroid Build Coastguard Worker
607*1fa6dee9SAndroid Build Coastguard Worker// propertyToValue creates a value of a given value type from the property.
608*1fa6dee9SAndroid Build Coastguard Workerfunc propertyToValue(typ reflect.Type, property *parser.Property) (reflect.Value, error) {
609*1fa6dee9SAndroid Build Coastguard Worker	var value reflect.Value
610*1fa6dee9SAndroid Build Coastguard Worker	var baseType reflect.Type
611*1fa6dee9SAndroid Build Coastguard Worker	isPtr := typ.Kind() == reflect.Ptr
612*1fa6dee9SAndroid Build Coastguard Worker	if isPtr {
613*1fa6dee9SAndroid Build Coastguard Worker		baseType = typ.Elem()
614*1fa6dee9SAndroid Build Coastguard Worker	} else {
615*1fa6dee9SAndroid Build Coastguard Worker		baseType = typ
616*1fa6dee9SAndroid Build Coastguard Worker	}
617*1fa6dee9SAndroid Build Coastguard Worker
618*1fa6dee9SAndroid Build Coastguard Worker	switch kind := baseType.Kind(); kind {
619*1fa6dee9SAndroid Build Coastguard Worker	case reflect.Bool:
620*1fa6dee9SAndroid Build Coastguard Worker		b, ok := property.Value.(*parser.Bool)
621*1fa6dee9SAndroid Build Coastguard Worker		if !ok {
622*1fa6dee9SAndroid Build Coastguard Worker			if err := selectOnNonConfigurablePropertyError(property); err != nil {
623*1fa6dee9SAndroid Build Coastguard Worker				return value, err
624*1fa6dee9SAndroid Build Coastguard Worker			} else {
625*1fa6dee9SAndroid Build Coastguard Worker				return value, &UnpackError{
626*1fa6dee9SAndroid Build Coastguard Worker					fmt.Errorf("can't assign %s value to bool property %q",
627*1fa6dee9SAndroid Build Coastguard Worker						property.Value.Type(), property.Name),
628*1fa6dee9SAndroid Build Coastguard Worker					property.Value.Pos(),
629*1fa6dee9SAndroid Build Coastguard Worker				}
630*1fa6dee9SAndroid Build Coastguard Worker			}
631*1fa6dee9SAndroid Build Coastguard Worker		}
632*1fa6dee9SAndroid Build Coastguard Worker		value = reflect.ValueOf(b.Value)
633*1fa6dee9SAndroid Build Coastguard Worker
634*1fa6dee9SAndroid Build Coastguard Worker	case reflect.Int64:
635*1fa6dee9SAndroid Build Coastguard Worker		b, ok := property.Value.(*parser.Int64)
636*1fa6dee9SAndroid Build Coastguard Worker		if !ok {
637*1fa6dee9SAndroid Build Coastguard Worker			return value, &UnpackError{
638*1fa6dee9SAndroid Build Coastguard Worker				fmt.Errorf("can't assign %s value to int64 property %q",
639*1fa6dee9SAndroid Build Coastguard Worker					property.Value.Type(), property.Name),
640*1fa6dee9SAndroid Build Coastguard Worker				property.Value.Pos(),
641*1fa6dee9SAndroid Build Coastguard Worker			}
642*1fa6dee9SAndroid Build Coastguard Worker		}
643*1fa6dee9SAndroid Build Coastguard Worker		value = reflect.ValueOf(b.Value)
644*1fa6dee9SAndroid Build Coastguard Worker
645*1fa6dee9SAndroid Build Coastguard Worker	case reflect.String:
646*1fa6dee9SAndroid Build Coastguard Worker		s, ok := property.Value.(*parser.String)
647*1fa6dee9SAndroid Build Coastguard Worker		if !ok {
648*1fa6dee9SAndroid Build Coastguard Worker			if err := selectOnNonConfigurablePropertyError(property); err != nil {
649*1fa6dee9SAndroid Build Coastguard Worker				return value, err
650*1fa6dee9SAndroid Build Coastguard Worker			} else {
651*1fa6dee9SAndroid Build Coastguard Worker				return value, &UnpackError{
652*1fa6dee9SAndroid Build Coastguard Worker					fmt.Errorf("can't assign %s value to string property %q",
653*1fa6dee9SAndroid Build Coastguard Worker						property.Value.Type(), property.Name),
654*1fa6dee9SAndroid Build Coastguard Worker					property.Value.Pos(),
655*1fa6dee9SAndroid Build Coastguard Worker				}
656*1fa6dee9SAndroid Build Coastguard Worker			}
657*1fa6dee9SAndroid Build Coastguard Worker		}
658*1fa6dee9SAndroid Build Coastguard Worker		value = reflect.ValueOf(s.Value)
659*1fa6dee9SAndroid Build Coastguard Worker
660*1fa6dee9SAndroid Build Coastguard Worker	default:
661*1fa6dee9SAndroid Build Coastguard Worker		return value, &UnpackError{
662*1fa6dee9SAndroid Build Coastguard Worker			fmt.Errorf("cannot assign %s value %s to %s property %s", property.Value.Type(), property.Value, kind, typ),
663*1fa6dee9SAndroid Build Coastguard Worker			property.NamePos}
664*1fa6dee9SAndroid Build Coastguard Worker	}
665*1fa6dee9SAndroid Build Coastguard Worker
666*1fa6dee9SAndroid Build Coastguard Worker	if isPtr {
667*1fa6dee9SAndroid Build Coastguard Worker		ptrValue := reflect.New(value.Type())
668*1fa6dee9SAndroid Build Coastguard Worker		ptrValue.Elem().Set(value)
669*1fa6dee9SAndroid Build Coastguard Worker		return ptrValue, nil
670*1fa6dee9SAndroid Build Coastguard Worker	}
671*1fa6dee9SAndroid Build Coastguard Worker	return value, nil
672*1fa6dee9SAndroid Build Coastguard Worker}
673