1// Copyright 2024 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 "slices" 21 22 "github.com/google/blueprint/parser" 23) 24 25func RepackProperties(props []interface{}) (*parser.Map, error) { 26 27 var dereferencedProps []reflect.Value 28 for _, rawProp := range props { 29 propStruct := reflect.ValueOf(rawProp) 30 if !isStructPtr(propStruct.Type()) { 31 return nil, fmt.Errorf("properties must be *struct, got %s", 32 propStruct.Type()) 33 } 34 propStruct = propStruct.Elem() 35 dereferencedProps = append(dereferencedProps, propStruct) 36 } 37 38 return repackStructs(dereferencedProps) 39} 40 41func repackStructs(props []reflect.Value) (*parser.Map, error) { 42 var allFieldNames []string 43 for _, prop := range props { 44 propType := prop.Type() 45 for i := 0; i < propType.NumField(); i++ { 46 field := propType.Field(i) 47 if !slices.Contains(allFieldNames, field.Name) { 48 allFieldNames = append(allFieldNames, field.Name) 49 } 50 } 51 } 52 53 result := &parser.Map{} 54 55 for _, fieldName := range allFieldNames { 56 var fields []reflect.Value 57 for _, prop := range props { 58 field := prop.FieldByName(fieldName) 59 if field.IsValid() { 60 fields = append(fields, field) 61 } 62 } 63 if err := assertFieldsEquivalent(fields); err != nil { 64 return nil, err 65 } 66 67 var expr parser.Expression 68 var field reflect.Value 69 for _, f := range fields { 70 if !isPropEmpty(f) { 71 field = f 72 break 73 } 74 } 75 if !field.IsValid() { 76 continue 77 } 78 if isStruct(field.Type()) && !isConfigurable(field.Type()) { 79 x, err := repackStructs(fields) 80 if err != nil { 81 return nil, err 82 } 83 if x != nil { 84 expr = x 85 } 86 } else { 87 x, err := fieldToExpr(field) 88 if err != nil { 89 return nil, err 90 } 91 if x != nil { 92 expr = *x 93 } 94 } 95 96 if expr != nil { 97 result.Properties = append(result.Properties, &parser.Property{ 98 Name: PropertyNameForField(fieldName), 99 Value: expr, 100 }) 101 } 102 } 103 104 return result, nil 105} 106 107func fieldToExpr(field reflect.Value) (*parser.Expression, error) { 108 if IsConfigurable(field.Type()) { 109 return field.Interface().(configurableReflection).toExpression() 110 } 111 if field.Kind() == reflect.Pointer { 112 if field.IsNil() { 113 return nil, nil 114 } 115 field = field.Elem() 116 } 117 switch field.Kind() { 118 case reflect.String: 119 var result parser.Expression = &parser.String{Value: field.String()} 120 return &result, nil 121 case reflect.Bool: 122 var result parser.Expression = &parser.Bool{Value: field.Bool()} 123 return &result, nil 124 case reflect.Int, reflect.Int64: 125 var result parser.Expression = &parser.Int64{Value: field.Int()} 126 return &result, nil 127 case reflect.Slice: 128 var contents []parser.Expression 129 for i := 0; i < field.Len(); i++ { 130 inner, err := fieldToExpr(field.Index(i)) 131 if err != nil { 132 return nil, err 133 } 134 contents = append(contents, *inner) 135 } 136 var result parser.Expression = &parser.List{Values: contents} 137 return &result, nil 138 case reflect.Struct: 139 var properties []*parser.Property 140 typ := field.Type() 141 for i := 0; i < typ.NumField(); i++ { 142 inner, err := fieldToExpr(field.Field(i)) 143 if err != nil { 144 return nil, err 145 } 146 properties = append(properties, &parser.Property{ 147 Name: typ.Field(i).Name, 148 Value: *inner, 149 }) 150 } 151 var result parser.Expression = &parser.Map{Properties: properties} 152 return &result, nil 153 default: 154 return nil, fmt.Errorf("Unhandled type: %s", field.Kind().String()) 155 } 156} 157 158func isPropEmpty(value reflect.Value) bool { 159 switch value.Kind() { 160 case reflect.Pointer: 161 if value.IsNil() { 162 return true 163 } 164 return isPropEmpty(value.Elem()) 165 case reflect.Struct: 166 if isConfigurable(value.Type()) { 167 return value.Interface().(configurableReflection).isEmpty() 168 } 169 for i := 0; i < value.NumField(); i++ { 170 if !isPropEmpty(value.Field(i)) { 171 return false 172 } 173 } 174 return true 175 default: 176 return false 177 } 178} 179 180func assertFieldsEquivalent(fields []reflect.Value) error { 181 var firstNonEmpty reflect.Value 182 var firstIndex int 183 for i, f := range fields { 184 if !isPropEmpty(f) { 185 firstNonEmpty = f 186 firstIndex = i 187 break 188 } 189 } 190 if !firstNonEmpty.IsValid() { 191 return nil 192 } 193 for i, f := range fields { 194 if i != firstIndex && !isPropEmpty(f) { 195 if err := assertTwoNonEmptyFieldsEquivalent(firstNonEmpty, f); err != nil { 196 return err 197 } 198 } 199 } 200 return nil 201} 202 203func assertTwoNonEmptyFieldsEquivalent(a, b reflect.Value) error { 204 aType := a.Type() 205 bType := b.Type() 206 207 if aType != bType { 208 return fmt.Errorf("fields must have the same type") 209 } 210 211 switch aType.Kind() { 212 case reflect.Pointer: 213 return assertTwoNonEmptyFieldsEquivalent(a.Elem(), b.Elem()) 214 case reflect.String: 215 if a.String() != b.String() { 216 return fmt.Errorf("Conflicting fields in property structs had values %q and %q", a.String(), b.String()) 217 } 218 case reflect.Bool: 219 if a.Bool() != b.Bool() { 220 return fmt.Errorf("Conflicting fields in property structs had values %t and %t", a.Bool(), b.Bool()) 221 } 222 case reflect.Slice: 223 if a.Len() != b.Len() { 224 return fmt.Errorf("Conflicting fields in property structs had lengths %d and %d", a.Len(), b.Len()) 225 } 226 for i := 0; i < a.Len(); i++ { 227 if err := assertTwoNonEmptyFieldsEquivalent(a.Index(i), b.Index(i)); err != nil { 228 return err 229 } 230 } 231 case reflect.Int: 232 if a.Int() != b.Int() { 233 return fmt.Errorf("Conflicting fields in property structs had values %d and %d", a.Int(), b.Int()) 234 } 235 case reflect.Struct: 236 if isConfigurable(a.Type()) { 237 // We could properly check that two configurables are equivalent, but that's a lot more 238 // work for a case that I don't think should show up in practice. 239 return fmt.Errorf("Cannot handle two property structs with nonempty configurable properties") 240 } 241 // We don't care about checking if structs are equivalent, we'll check their individual 242 // fields when we recurse down. 243 default: 244 return fmt.Errorf("Unhandled kind: %s", aType.Kind().String()) 245 } 246 247 return nil 248} 249