Matteo Scandolo | a428586 | 2020-12-01 18:10:10 -0800 | [diff] [blame] | 1 | /* |
| 2 | Copyright 2019 The Kubernetes Authors. |
| 3 | |
| 4 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | you may not use this file except in compliance with the License. |
| 6 | You may obtain a copy of the License at |
| 7 | |
| 8 | http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | |
| 10 | Unless required by applicable law or agreed to in writing, software |
| 11 | distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | See the License for the specific language governing permissions and |
| 14 | limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package value |
| 18 | |
| 19 | import ( |
| 20 | "encoding/base64" |
| 21 | "fmt" |
| 22 | "reflect" |
| 23 | ) |
| 24 | |
| 25 | // NewValueReflect creates a Value backed by an "interface{}" type, |
| 26 | // typically an structured object in Kubernetes world that is uses reflection to expose. |
| 27 | // The provided "interface{}" value must be a pointer so that the value can be modified via reflection. |
| 28 | // The provided "interface{}" may contain structs and types that are converted to Values |
| 29 | // by the jsonMarshaler interface. |
| 30 | func NewValueReflect(value interface{}) (Value, error) { |
| 31 | if value == nil { |
| 32 | return NewValueInterface(nil), nil |
| 33 | } |
| 34 | v := reflect.ValueOf(value) |
| 35 | if v.Kind() != reflect.Ptr { |
| 36 | // The root value to reflect on must be a pointer so that map.Set() and map.Delete() operations are possible. |
| 37 | return nil, fmt.Errorf("value provided to NewValueReflect must be a pointer") |
| 38 | } |
| 39 | return wrapValueReflect(v, nil, nil) |
| 40 | } |
| 41 | |
| 42 | // wrapValueReflect wraps the provide reflect.Value as a value. If parent in the data tree is a map, parentMap |
| 43 | // and parentMapKey must be provided so that the returned value may be set and deleted. |
| 44 | func wrapValueReflect(value reflect.Value, parentMap, parentMapKey *reflect.Value) (Value, error) { |
| 45 | val := HeapAllocator.allocValueReflect() |
| 46 | return val.reuse(value, nil, parentMap, parentMapKey) |
| 47 | } |
| 48 | |
| 49 | // wrapValueReflect wraps the provide reflect.Value as a value, and panics if there is an error. If parent in the data |
| 50 | // tree is a map, parentMap and parentMapKey must be provided so that the returned value may be set and deleted. |
| 51 | func mustWrapValueReflect(value reflect.Value, parentMap, parentMapKey *reflect.Value) Value { |
| 52 | v, err := wrapValueReflect(value, parentMap, parentMapKey) |
| 53 | if err != nil { |
| 54 | panic(err) |
| 55 | } |
| 56 | return v |
| 57 | } |
| 58 | |
| 59 | // the value interface doesn't care about the type for value.IsNull, so we can use a constant |
| 60 | var nilType = reflect.TypeOf(&struct{}{}) |
| 61 | |
| 62 | // reuse replaces the value of the valueReflect. If parent in the data tree is a map, parentMap and parentMapKey |
| 63 | // must be provided so that the returned value may be set and deleted. |
| 64 | func (r *valueReflect) reuse(value reflect.Value, cacheEntry *TypeReflectCacheEntry, parentMap, parentMapKey *reflect.Value) (Value, error) { |
| 65 | if cacheEntry == nil { |
| 66 | cacheEntry = TypeReflectEntryOf(value.Type()) |
| 67 | } |
| 68 | if cacheEntry.CanConvertToUnstructured() { |
| 69 | u, err := cacheEntry.ToUnstructured(value) |
| 70 | if err != nil { |
| 71 | return nil, err |
| 72 | } |
| 73 | if u == nil { |
| 74 | value = reflect.Zero(nilType) |
| 75 | } else { |
| 76 | value = reflect.ValueOf(u) |
| 77 | } |
| 78 | } |
| 79 | r.Value = dereference(value) |
| 80 | r.ParentMap = parentMap |
| 81 | r.ParentMapKey = parentMapKey |
| 82 | r.kind = kind(r.Value) |
| 83 | return r, nil |
| 84 | } |
| 85 | |
| 86 | // mustReuse replaces the value of the valueReflect and panics if there is an error. If parent in the data tree is a |
| 87 | // map, parentMap and parentMapKey must be provided so that the returned value may be set and deleted. |
| 88 | func (r *valueReflect) mustReuse(value reflect.Value, cacheEntry *TypeReflectCacheEntry, parentMap, parentMapKey *reflect.Value) Value { |
| 89 | v, err := r.reuse(value, cacheEntry, parentMap, parentMapKey) |
| 90 | if err != nil { |
| 91 | panic(err) |
| 92 | } |
| 93 | return v |
| 94 | } |
| 95 | |
| 96 | func dereference(val reflect.Value) reflect.Value { |
| 97 | kind := val.Kind() |
| 98 | if (kind == reflect.Interface || kind == reflect.Ptr) && !safeIsNil(val) { |
| 99 | return val.Elem() |
| 100 | } |
| 101 | return val |
| 102 | } |
| 103 | |
| 104 | type valueReflect struct { |
| 105 | ParentMap *reflect.Value |
| 106 | ParentMapKey *reflect.Value |
| 107 | Value reflect.Value |
| 108 | kind reflectType |
| 109 | } |
| 110 | |
| 111 | func (r valueReflect) IsMap() bool { |
| 112 | return r.kind == mapType || r.kind == structMapType |
| 113 | } |
| 114 | |
| 115 | func (r valueReflect) IsList() bool { |
| 116 | return r.kind == listType |
| 117 | } |
| 118 | |
| 119 | func (r valueReflect) IsBool() bool { |
| 120 | return r.kind == boolType |
| 121 | } |
| 122 | |
| 123 | func (r valueReflect) IsInt() bool { |
| 124 | return r.kind == intType || r.kind == uintType |
| 125 | } |
| 126 | |
| 127 | func (r valueReflect) IsFloat() bool { |
| 128 | return r.kind == floatType |
| 129 | } |
| 130 | |
| 131 | func (r valueReflect) IsString() bool { |
| 132 | return r.kind == stringType || r.kind == byteStringType |
| 133 | } |
| 134 | |
| 135 | func (r valueReflect) IsNull() bool { |
| 136 | return r.kind == nullType |
| 137 | } |
| 138 | |
| 139 | type reflectType = int |
| 140 | |
| 141 | const ( |
| 142 | mapType = iota |
| 143 | structMapType |
| 144 | listType |
| 145 | intType |
| 146 | uintType |
| 147 | floatType |
| 148 | stringType |
| 149 | byteStringType |
| 150 | boolType |
| 151 | nullType |
| 152 | ) |
| 153 | |
| 154 | func kind(v reflect.Value) reflectType { |
| 155 | typ := v.Type() |
| 156 | rk := typ.Kind() |
| 157 | switch rk { |
| 158 | case reflect.Map: |
| 159 | if v.IsNil() { |
| 160 | return nullType |
| 161 | } |
| 162 | return mapType |
| 163 | case reflect.Struct: |
| 164 | return structMapType |
| 165 | case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: |
| 166 | return intType |
| 167 | case reflect.Uint, reflect.Uint32, reflect.Uint16, reflect.Uint8: |
| 168 | // Uint64 deliberately excluded, see valueUnstructured.Int. |
| 169 | return uintType |
| 170 | case reflect.Float64, reflect.Float32: |
| 171 | return floatType |
| 172 | case reflect.String: |
| 173 | return stringType |
| 174 | case reflect.Bool: |
| 175 | return boolType |
| 176 | case reflect.Slice: |
| 177 | if v.IsNil() { |
| 178 | return nullType |
| 179 | } |
| 180 | elemKind := typ.Elem().Kind() |
| 181 | if elemKind == reflect.Uint8 { |
| 182 | return byteStringType |
| 183 | } |
| 184 | return listType |
| 185 | case reflect.Chan, reflect.Func, reflect.Ptr, reflect.UnsafePointer, reflect.Interface: |
| 186 | if v.IsNil() { |
| 187 | return nullType |
| 188 | } |
| 189 | panic(fmt.Sprintf("unsupported type: %v", v.Type())) |
| 190 | default: |
| 191 | panic(fmt.Sprintf("unsupported type: %v", v.Type())) |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | // TODO find a cleaner way to avoid panics from reflect.IsNil() |
| 196 | func safeIsNil(v reflect.Value) bool { |
| 197 | k := v.Kind() |
| 198 | switch k { |
| 199 | case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice: |
| 200 | return v.IsNil() |
| 201 | } |
| 202 | return false |
| 203 | } |
| 204 | |
| 205 | func (r valueReflect) AsMap() Map { |
| 206 | return r.AsMapUsing(HeapAllocator) |
| 207 | } |
| 208 | |
| 209 | func (r valueReflect) AsMapUsing(a Allocator) Map { |
| 210 | switch r.kind { |
| 211 | case structMapType: |
| 212 | v := a.allocStructReflect() |
| 213 | v.valueReflect = r |
| 214 | return v |
| 215 | case mapType: |
| 216 | v := a.allocMapReflect() |
| 217 | v.valueReflect = r |
| 218 | return v |
| 219 | default: |
| 220 | panic("value is not a map or struct") |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | func (r valueReflect) AsList() List { |
| 225 | return r.AsListUsing(HeapAllocator) |
| 226 | } |
| 227 | |
| 228 | func (r valueReflect) AsListUsing(a Allocator) List { |
| 229 | if r.IsList() { |
| 230 | v := a.allocListReflect() |
| 231 | v.Value = r.Value |
| 232 | return v |
| 233 | } |
| 234 | panic("value is not a list") |
| 235 | } |
| 236 | |
| 237 | func (r valueReflect) AsBool() bool { |
| 238 | if r.IsBool() { |
| 239 | return r.Value.Bool() |
| 240 | } |
| 241 | panic("value is not a bool") |
| 242 | } |
| 243 | |
| 244 | func (r valueReflect) AsInt() int64 { |
| 245 | if r.kind == intType { |
| 246 | return r.Value.Int() |
| 247 | } |
| 248 | if r.kind == uintType { |
| 249 | return int64(r.Value.Uint()) |
| 250 | } |
| 251 | |
| 252 | panic("value is not an int") |
| 253 | } |
| 254 | |
| 255 | func (r valueReflect) AsFloat() float64 { |
| 256 | if r.IsFloat() { |
| 257 | return r.Value.Float() |
| 258 | } |
| 259 | panic("value is not a float") |
| 260 | } |
| 261 | |
| 262 | func (r valueReflect) AsString() string { |
| 263 | switch r.kind { |
| 264 | case stringType: |
| 265 | return r.Value.String() |
| 266 | case byteStringType: |
| 267 | return base64.StdEncoding.EncodeToString(r.Value.Bytes()) |
| 268 | } |
| 269 | panic("value is not a string") |
| 270 | } |
| 271 | |
| 272 | func (r valueReflect) Unstructured() interface{} { |
| 273 | val := r.Value |
| 274 | switch { |
| 275 | case r.IsNull(): |
| 276 | return nil |
| 277 | case val.Kind() == reflect.Struct: |
| 278 | return structReflect{r}.Unstructured() |
| 279 | case val.Kind() == reflect.Map: |
| 280 | return mapReflect{valueReflect: r}.Unstructured() |
| 281 | case r.IsList(): |
| 282 | return listReflect{r.Value}.Unstructured() |
| 283 | case r.IsString(): |
| 284 | return r.AsString() |
| 285 | case r.IsInt(): |
| 286 | return r.AsInt() |
| 287 | case r.IsBool(): |
| 288 | return r.AsBool() |
| 289 | case r.IsFloat(): |
| 290 | return r.AsFloat() |
| 291 | default: |
| 292 | panic(fmt.Sprintf("value of type %s is not a supported by value reflector", val.Type())) |
| 293 | } |
| 294 | } |