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