1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package impl
6
7import (
8 "fmt"
9 "math"
10 "reflect"
11
12 "google.golang.org/protobuf/reflect/protoreflect"
13)
14
15type fieldInfo struct {
16 fieldDesc protoreflect.FieldDescriptor
17
18 // These fields are used for protobuf reflection support.
19 has func(pointer) bool
20 clear func(pointer)
21 get func(pointer) protoreflect.Value
22 set func(pointer, protoreflect.Value)
23 mutable func(pointer) protoreflect.Value
24 newMessage func() protoreflect.Message
25 newField func() protoreflect.Value
26}
27
28func fieldInfoForMissing(fd protoreflect.FieldDescriptor) fieldInfo {
29 // This never occurs for generated message types.
30 // It implies that a hand-crafted type has missing Go fields
31 // for specific protobuf message fields.
32 return fieldInfo{
33 fieldDesc: fd,
34 has: func(p pointer) bool {
35 return false
36 },
37 clear: func(p pointer) {
38 panic("missing Go struct field for " + string(fd.FullName()))
39 },
40 get: func(p pointer) protoreflect.Value {
41 return fd.Default()
42 },
43 set: func(p pointer, v protoreflect.Value) {
44 panic("missing Go struct field for " + string(fd.FullName()))
45 },
46 mutable: func(p pointer) protoreflect.Value {
47 panic("missing Go struct field for " + string(fd.FullName()))
48 },
49 newMessage: func() protoreflect.Message {
50 panic("missing Go struct field for " + string(fd.FullName()))
51 },
52 newField: func() protoreflect.Value {
53 if v := fd.Default(); v.IsValid() {
54 return v
55 }
56 panic("missing Go struct field for " + string(fd.FullName()))
57 },
58 }
59}
60
61func fieldInfoForOneof(fd protoreflect.FieldDescriptor, fs reflect.StructField, x exporter, ot reflect.Type) fieldInfo {
62 ft := fs.Type
63 if ft.Kind() != reflect.Interface {
64 panic(fmt.Sprintf("field %v has invalid type: got %v, want interface kind", fd.FullName(), ft))
65 }
66 if ot.Kind() != reflect.Struct {
67 panic(fmt.Sprintf("field %v has invalid type: got %v, want struct kind", fd.FullName(), ot))
68 }
69 if !reflect.PtrTo(ot).Implements(ft) {
70 panic(fmt.Sprintf("field %v has invalid type: %v does not implement %v", fd.FullName(), ot, ft))
71 }
72 conv := NewConverter(ot.Field(0).Type, fd)
73 isMessage := fd.Message() != nil
74
75 // TODO: Implement unsafe fast path?
76 fieldOffset := offsetOf(fs)
77 return fieldInfo{
78 // NOTE: The logic below intentionally assumes that oneof fields are
79 // well-formatted. That is, the oneof interface never contains a
80 // typed nil pointer to one of the wrapper structs.
81
82 fieldDesc: fd,
83 has: func(p pointer) bool {
84 if p.IsNil() {
85 return false
86 }
87 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
88 if rv.IsNil() || rv.Elem().Type().Elem() != ot || rv.Elem().IsNil() {
89 return false
90 }
91 return true
92 },
93 clear: func(p pointer) {
94 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
95 if rv.IsNil() || rv.Elem().Type().Elem() != ot {
96 // NOTE: We intentionally don't check for rv.Elem().IsNil()
97 // so that (*OneofWrapperType)(nil) gets cleared to nil.
98 return
99 }
100 rv.Set(reflect.Zero(rv.Type()))
101 },
102 get: func(p pointer) protoreflect.Value {
103 if p.IsNil() {
104 return conv.Zero()
105 }
106 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
107 if rv.IsNil() || rv.Elem().Type().Elem() != ot || rv.Elem().IsNil() {
108 return conv.Zero()
109 }
110 rv = rv.Elem().Elem().Field(0)
111 return conv.PBValueOf(rv)
112 },
113 set: func(p pointer, v protoreflect.Value) {
114 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
115 if rv.IsNil() || rv.Elem().Type().Elem() != ot || rv.Elem().IsNil() {
116 rv.Set(reflect.New(ot))
117 }
118 rv = rv.Elem().Elem().Field(0)
119 rv.Set(conv.GoValueOf(v))
120 },
121 mutable: func(p pointer) protoreflect.Value {
122 if !isMessage {
123 panic(fmt.Sprintf("field %v with invalid Mutable call on field with non-composite type", fd.FullName()))
124 }
125 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
126 if rv.IsNil() || rv.Elem().Type().Elem() != ot || rv.Elem().IsNil() {
127 rv.Set(reflect.New(ot))
128 }
129 rv = rv.Elem().Elem().Field(0)
130 if rv.Kind() == reflect.Ptr && rv.IsNil() {
131 rv.Set(conv.GoValueOf(protoreflect.ValueOfMessage(conv.New().Message())))
132 }
133 return conv.PBValueOf(rv)
134 },
135 newMessage: func() protoreflect.Message {
136 return conv.New().Message()
137 },
138 newField: func() protoreflect.Value {
139 return conv.New()
140 },
141 }
142}
143
144func fieldInfoForMap(fd protoreflect.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
145 ft := fs.Type
146 if ft.Kind() != reflect.Map {
147 panic(fmt.Sprintf("field %v has invalid type: got %v, want map kind", fd.FullName(), ft))
148 }
149 conv := NewConverter(ft, fd)
150
151 // TODO: Implement unsafe fast path?
152 fieldOffset := offsetOf(fs)
153 return fieldInfo{
154 fieldDesc: fd,
155 has: func(p pointer) bool {
156 if p.IsNil() {
157 return false
158 }
159 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
160 return rv.Len() > 0
161 },
162 clear: func(p pointer) {
163 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
164 rv.Set(reflect.Zero(rv.Type()))
165 },
166 get: func(p pointer) protoreflect.Value {
167 if p.IsNil() {
168 return conv.Zero()
169 }
170 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
171 if rv.Len() == 0 {
172 return conv.Zero()
173 }
174 return conv.PBValueOf(rv)
175 },
176 set: func(p pointer, v protoreflect.Value) {
177 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
178 pv := conv.GoValueOf(v)
179 if pv.IsNil() {
180 panic(fmt.Sprintf("map field %v cannot be set with read-only value", fd.FullName()))
181 }
182 rv.Set(pv)
183 },
184 mutable: func(p pointer) protoreflect.Value {
185 v := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
186 if v.IsNil() {
187 v.Set(reflect.MakeMap(fs.Type))
188 }
189 return conv.PBValueOf(v)
190 },
191 newField: func() protoreflect.Value {
192 return conv.New()
193 },
194 }
195}
196
197func fieldInfoForList(fd protoreflect.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
198 ft := fs.Type
199 if ft.Kind() != reflect.Slice {
200 panic(fmt.Sprintf("field %v has invalid type: got %v, want slice kind", fd.FullName(), ft))
201 }
202 conv := NewConverter(reflect.PtrTo(ft), fd)
203
204 // TODO: Implement unsafe fast path?
205 fieldOffset := offsetOf(fs)
206 return fieldInfo{
207 fieldDesc: fd,
208 has: func(p pointer) bool {
209 if p.IsNil() {
210 return false
211 }
212 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
213 return rv.Len() > 0
214 },
215 clear: func(p pointer) {
216 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
217 rv.Set(reflect.Zero(rv.Type()))
218 },
219 get: func(p pointer) protoreflect.Value {
220 if p.IsNil() {
221 return conv.Zero()
222 }
223 rv := p.Apply(fieldOffset).AsValueOf(fs.Type)
224 if rv.Elem().Len() == 0 {
225 return conv.Zero()
226 }
227 return conv.PBValueOf(rv)
228 },
229 set: func(p pointer, v protoreflect.Value) {
230 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
231 pv := conv.GoValueOf(v)
232 if pv.IsNil() {
233 panic(fmt.Sprintf("list field %v cannot be set with read-only value", fd.FullName()))
234 }
235 rv.Set(pv.Elem())
236 },
237 mutable: func(p pointer) protoreflect.Value {
238 v := p.Apply(fieldOffset).AsValueOf(fs.Type)
239 return conv.PBValueOf(v)
240 },
241 newField: func() protoreflect.Value {
242 return conv.New()
243 },
244 }
245}
246
247var (
248 nilBytes = reflect.ValueOf([]byte(nil))
249 emptyBytes = reflect.ValueOf([]byte{})
250)
251
252func fieldInfoForScalar(fd protoreflect.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
253 ft := fs.Type
254 nullable := fd.HasPresence()
255 isBytes := ft.Kind() == reflect.Slice && ft.Elem().Kind() == reflect.Uint8
256 var getter func(p pointer) protoreflect.Value
257 if nullable {
258 if ft.Kind() != reflect.Ptr && ft.Kind() != reflect.Slice {
259 // This never occurs for generated message types.
260 // Despite the protobuf type system specifying presence,
261 // the Go field type cannot represent it.
262 nullable = false
263 }
264 if ft.Kind() == reflect.Ptr {
265 ft = ft.Elem()
266 }
267 }
268 conv := NewConverter(ft, fd)
269 fieldOffset := offsetOf(fs)
270
271 // Generate specialized getter functions to avoid going through reflect.Value
272 if nullable {
273 getter = getterForNullableScalar(fd, fs, conv, fieldOffset)
274 } else {
275 getter = getterForDirectScalar(fd, fs, conv, fieldOffset)
276 }
277
278 return fieldInfo{
279 fieldDesc: fd,
280 has: func(p pointer) bool {
281 if p.IsNil() {
282 return false
283 }
284 if nullable {
285 return !p.Apply(fieldOffset).Elem().IsNil()
286 }
287 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
288 switch rv.Kind() {
289 case reflect.Bool:
290 return rv.Bool()
291 case reflect.Int32, reflect.Int64:
292 return rv.Int() != 0
293 case reflect.Uint32, reflect.Uint64:
294 return rv.Uint() != 0
295 case reflect.Float32, reflect.Float64:
296 return rv.Float() != 0 || math.Signbit(rv.Float())
297 case reflect.String, reflect.Slice:
298 return rv.Len() > 0
299 default:
300 panic(fmt.Sprintf("field %v has invalid type: %v", fd.FullName(), rv.Type())) // should never happen
301 }
302 },
303 clear: func(p pointer) {
304 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
305 rv.Set(reflect.Zero(rv.Type()))
306 },
307 get: getter,
308 // TODO: Implement unsafe fast path for set?
309 set: func(p pointer, v protoreflect.Value) {
310 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
311 if nullable && rv.Kind() == reflect.Ptr {
312 if rv.IsNil() {
313 rv.Set(reflect.New(ft))
314 }
315 rv = rv.Elem()
316 }
317 rv.Set(conv.GoValueOf(v))
318 if isBytes && rv.Len() == 0 {
319 if nullable {
320 rv.Set(emptyBytes) // preserve presence
321 } else {
322 rv.Set(nilBytes) // do not preserve presence
323 }
324 }
325 },
326 newField: func() protoreflect.Value {
327 return conv.New()
328 },
329 }
330}
331
332func fieldInfoForMessage(fd protoreflect.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
333 ft := fs.Type
334 conv := NewConverter(ft, fd)
335
336 // TODO: Implement unsafe fast path?
337 fieldOffset := offsetOf(fs)
338 return fieldInfo{
339 fieldDesc: fd,
340 has: func(p pointer) bool {
341 if p.IsNil() {
342 return false
343 }
344 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
345 if fs.Type.Kind() != reflect.Ptr {
346 return !rv.IsZero()
347 }
348 return !rv.IsNil()
349 },
350 clear: func(p pointer) {
351 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
352 rv.Set(reflect.Zero(rv.Type()))
353 },
354 get: func(p pointer) protoreflect.Value {
355 if p.IsNil() {
356 return conv.Zero()
357 }
358 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
359 return conv.PBValueOf(rv)
360 },
361 set: func(p pointer, v protoreflect.Value) {
362 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
363 rv.Set(conv.GoValueOf(v))
364 if fs.Type.Kind() == reflect.Ptr && rv.IsNil() {
365 panic(fmt.Sprintf("field %v has invalid nil pointer", fd.FullName()))
366 }
367 },
368 mutable: func(p pointer) protoreflect.Value {
369 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
370 if fs.Type.Kind() == reflect.Ptr && rv.IsNil() {
371 rv.Set(conv.GoValueOf(conv.New()))
372 }
373 return conv.PBValueOf(rv)
374 },
375 newMessage: func() protoreflect.Message {
376 return conv.New().Message()
377 },
378 newField: func() protoreflect.Value {
379 return conv.New()
380 },
381 }
382}
383
384type oneofInfo struct {
385 oneofDesc protoreflect.OneofDescriptor
386 which func(pointer) protoreflect.FieldNumber
387}
388
389func makeOneofInfo(od protoreflect.OneofDescriptor, si structInfo, x exporter) *oneofInfo {
390 oi := &oneofInfo{oneofDesc: od}
391 if od.IsSynthetic() {
392 fs := si.fieldsByNumber[od.Fields().Get(0).Number()]
393 fieldOffset := offsetOf(fs)
394 oi.which = func(p pointer) protoreflect.FieldNumber {
395 if p.IsNil() {
396 return 0
397 }
398 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
399 if rv.IsNil() { // valid on either *T or []byte
400 return 0
401 }
402 return od.Fields().Get(0).Number()
403 }
404 } else {
405 fs := si.oneofsByName[od.Name()]
406 fieldOffset := offsetOf(fs)
407 oi.which = func(p pointer) protoreflect.FieldNumber {
408 if p.IsNil() {
409 return 0
410 }
411 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
412 if rv.IsNil() {
413 return 0
414 }
415 rv = rv.Elem()
416 if rv.IsNil() {
417 return 0
418 }
419 return si.oneofWrappersByType[rv.Type().Elem()]
420 }
421 }
422 return oi
423}