blob: 05e70debaef3e643dcf81350c3433dd87c3e104e [file] [log] [blame]
Matteo Scandoloa4285862020-12-01 18:10:10 -08001/*
2Copyright 2019 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package value
18
19import (
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.
30func 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.
44func 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.
51func 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
60var 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.
64func (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.
88func (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
96func 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
104type valueReflect struct {
105 ParentMap *reflect.Value
106 ParentMapKey *reflect.Value
107 Value reflect.Value
108 kind reflectType
109}
110
111func (r valueReflect) IsMap() bool {
112 return r.kind == mapType || r.kind == structMapType
113}
114
115func (r valueReflect) IsList() bool {
116 return r.kind == listType
117}
118
119func (r valueReflect) IsBool() bool {
120 return r.kind == boolType
121}
122
123func (r valueReflect) IsInt() bool {
124 return r.kind == intType || r.kind == uintType
125}
126
127func (r valueReflect) IsFloat() bool {
128 return r.kind == floatType
129}
130
131func (r valueReflect) IsString() bool {
132 return r.kind == stringType || r.kind == byteStringType
133}
134
135func (r valueReflect) IsNull() bool {
136 return r.kind == nullType
137}
138
139type reflectType = int
140
141const (
142 mapType = iota
143 structMapType
144 listType
145 intType
146 uintType
147 floatType
148 stringType
149 byteStringType
150 boolType
151 nullType
152)
153
154func 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()
196func 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
205func (r valueReflect) AsMap() Map {
206 return r.AsMapUsing(HeapAllocator)
207}
208
209func (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
224func (r valueReflect) AsList() List {
225 return r.AsListUsing(HeapAllocator)
226}
227
228func (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
237func (r valueReflect) AsBool() bool {
238 if r.IsBool() {
239 return r.Value.Bool()
240 }
241 panic("value is not a bool")
242}
243
244func (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
255func (r valueReflect) AsFloat() float64 {
256 if r.IsFloat() {
257 return r.Value.Float()
258 }
259 panic("value is not a float")
260}
261
262func (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
272func (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}