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