blob: 7b87d4712f27009e726e4a3dbe168dd295784402 [file] [log] [blame]
Scott Baker105df152020-04-13 15:55:14 -07001// 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 "sync"
12
13 "google.golang.org/protobuf/internal/flags"
14 pref "google.golang.org/protobuf/reflect/protoreflect"
15 preg "google.golang.org/protobuf/reflect/protoregistry"
16)
17
18type fieldInfo struct {
19 fieldDesc pref.FieldDescriptor
20
21 // These fields are used for protobuf reflection support.
22 has func(pointer) bool
23 clear func(pointer)
24 get func(pointer) pref.Value
25 set func(pointer, pref.Value)
26 mutable func(pointer) pref.Value
27 newMessage func() pref.Message
28 newField func() pref.Value
29}
30
31func fieldInfoForOneof(fd pref.FieldDescriptor, fs reflect.StructField, x exporter, ot reflect.Type) fieldInfo {
32 ft := fs.Type
33 if ft.Kind() != reflect.Interface {
34 panic(fmt.Sprintf("invalid type: got %v, want interface kind", ft))
35 }
36 if ot.Kind() != reflect.Struct {
37 panic(fmt.Sprintf("invalid type: got %v, want struct kind", ot))
38 }
39 if !reflect.PtrTo(ot).Implements(ft) {
40 panic(fmt.Sprintf("invalid type: %v does not implement %v", ot, ft))
41 }
42 conv := NewConverter(ot.Field(0).Type, fd)
43 isMessage := fd.Message() != nil
44
45 // TODO: Implement unsafe fast path?
46 fieldOffset := offsetOf(fs, x)
47 return fieldInfo{
48 // NOTE: The logic below intentionally assumes that oneof fields are
49 // well-formatted. That is, the oneof interface never contains a
50 // typed nil pointer to one of the wrapper structs.
51
52 fieldDesc: fd,
53 has: func(p pointer) bool {
54 if p.IsNil() {
55 return false
56 }
57 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
58 if rv.IsNil() || rv.Elem().Type().Elem() != ot || rv.Elem().IsNil() {
59 return false
60 }
61 return true
62 },
63 clear: func(p pointer) {
64 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
65 if rv.IsNil() || rv.Elem().Type().Elem() != ot {
66 // NOTE: We intentionally don't check for rv.Elem().IsNil()
67 // so that (*OneofWrapperType)(nil) gets cleared to nil.
68 return
69 }
70 rv.Set(reflect.Zero(rv.Type()))
71 },
72 get: func(p pointer) pref.Value {
73 if p.IsNil() {
74 return conv.Zero()
75 }
76 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
77 if rv.IsNil() || rv.Elem().Type().Elem() != ot || rv.Elem().IsNil() {
78 return conv.Zero()
79 }
80 rv = rv.Elem().Elem().Field(0)
81 return conv.PBValueOf(rv)
82 },
83 set: func(p pointer, v pref.Value) {
84 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
85 if rv.IsNil() || rv.Elem().Type().Elem() != ot || rv.Elem().IsNil() {
86 rv.Set(reflect.New(ot))
87 }
88 rv = rv.Elem().Elem().Field(0)
89 rv.Set(conv.GoValueOf(v))
90 },
91 mutable: func(p pointer) pref.Value {
92 if !isMessage {
93 panic("invalid Mutable on field with non-composite type")
94 }
95 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
96 if rv.IsNil() || rv.Elem().Type().Elem() != ot || rv.Elem().IsNil() {
97 rv.Set(reflect.New(ot))
98 }
99 rv = rv.Elem().Elem().Field(0)
100 if rv.IsNil() {
101 rv.Set(conv.GoValueOf(pref.ValueOfMessage(conv.New().Message())))
102 }
103 return conv.PBValueOf(rv)
104 },
105 newMessage: func() pref.Message {
106 return conv.New().Message()
107 },
108 newField: func() pref.Value {
109 return conv.New()
110 },
111 }
112}
113
114func fieldInfoForMap(fd pref.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
115 ft := fs.Type
116 if ft.Kind() != reflect.Map {
117 panic(fmt.Sprintf("invalid type: got %v, want map kind", ft))
118 }
119 conv := NewConverter(ft, fd)
120
121 // TODO: Implement unsafe fast path?
122 fieldOffset := offsetOf(fs, x)
123 return fieldInfo{
124 fieldDesc: fd,
125 has: func(p pointer) bool {
126 if p.IsNil() {
127 return false
128 }
129 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
130 return rv.Len() > 0
131 },
132 clear: func(p pointer) {
133 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
134 rv.Set(reflect.Zero(rv.Type()))
135 },
136 get: func(p pointer) pref.Value {
137 if p.IsNil() {
138 return conv.Zero()
139 }
140 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
141 if rv.Len() == 0 {
142 return conv.Zero()
143 }
144 return conv.PBValueOf(rv)
145 },
146 set: func(p pointer, v pref.Value) {
147 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
148 pv := conv.GoValueOf(v)
149 if pv.IsNil() {
150 panic(fmt.Sprintf("invalid value: setting map field to read-only value"))
151 }
152 rv.Set(pv)
153 },
154 mutable: func(p pointer) pref.Value {
155 v := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
156 if v.IsNil() {
157 v.Set(reflect.MakeMap(fs.Type))
158 }
159 return conv.PBValueOf(v)
160 },
161 newField: func() pref.Value {
162 return conv.New()
163 },
164 }
165}
166
167func fieldInfoForList(fd pref.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
168 ft := fs.Type
169 if ft.Kind() != reflect.Slice {
170 panic(fmt.Sprintf("invalid type: got %v, want slice kind", ft))
171 }
172 conv := NewConverter(reflect.PtrTo(ft), fd)
173
174 // TODO: Implement unsafe fast path?
175 fieldOffset := offsetOf(fs, x)
176 return fieldInfo{
177 fieldDesc: fd,
178 has: func(p pointer) bool {
179 if p.IsNil() {
180 return false
181 }
182 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
183 return rv.Len() > 0
184 },
185 clear: func(p pointer) {
186 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
187 rv.Set(reflect.Zero(rv.Type()))
188 },
189 get: func(p pointer) pref.Value {
190 if p.IsNil() {
191 return conv.Zero()
192 }
193 rv := p.Apply(fieldOffset).AsValueOf(fs.Type)
194 if rv.Elem().Len() == 0 {
195 return conv.Zero()
196 }
197 return conv.PBValueOf(rv)
198 },
199 set: func(p pointer, v pref.Value) {
200 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
201 pv := conv.GoValueOf(v)
202 if pv.IsNil() {
203 panic(fmt.Sprintf("invalid value: setting repeated field to read-only value"))
204 }
205 rv.Set(pv.Elem())
206 },
207 mutable: func(p pointer) pref.Value {
208 v := p.Apply(fieldOffset).AsValueOf(fs.Type)
209 return conv.PBValueOf(v)
210 },
211 newField: func() pref.Value {
212 return conv.New()
213 },
214 }
215}
216
217var (
218 nilBytes = reflect.ValueOf([]byte(nil))
219 emptyBytes = reflect.ValueOf([]byte{})
220)
221
222func fieldInfoForScalar(fd pref.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
223 ft := fs.Type
224 nullable := fd.Syntax() == pref.Proto2
225 isBytes := ft.Kind() == reflect.Slice && ft.Elem().Kind() == reflect.Uint8
226 if nullable {
227 if ft.Kind() != reflect.Ptr && ft.Kind() != reflect.Slice {
228 panic(fmt.Sprintf("invalid type: got %v, want pointer", ft))
229 }
230 if ft.Kind() == reflect.Ptr {
231 ft = ft.Elem()
232 }
233 }
234 conv := NewConverter(ft, fd)
235
236 // TODO: Implement unsafe fast path?
237 fieldOffset := offsetOf(fs, x)
238 return fieldInfo{
239 fieldDesc: fd,
240 has: func(p pointer) bool {
241 if p.IsNil() {
242 return false
243 }
244 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
245 if nullable {
246 return !rv.IsNil()
247 }
248 switch rv.Kind() {
249 case reflect.Bool:
250 return rv.Bool()
251 case reflect.Int32, reflect.Int64:
252 return rv.Int() != 0
253 case reflect.Uint32, reflect.Uint64:
254 return rv.Uint() != 0
255 case reflect.Float32, reflect.Float64:
256 return rv.Float() != 0 || math.Signbit(rv.Float())
257 case reflect.String, reflect.Slice:
258 return rv.Len() > 0
259 default:
260 panic(fmt.Sprintf("invalid type: %v", rv.Type())) // should never happen
261 }
262 },
263 clear: func(p pointer) {
264 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
265 rv.Set(reflect.Zero(rv.Type()))
266 },
267 get: func(p pointer) pref.Value {
268 if p.IsNil() {
269 return conv.Zero()
270 }
271 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
272 if nullable {
273 if rv.IsNil() {
274 return conv.Zero()
275 }
276 if rv.Kind() == reflect.Ptr {
277 rv = rv.Elem()
278 }
279 }
280 return conv.PBValueOf(rv)
281 },
282 set: func(p pointer, v pref.Value) {
283 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
284 if nullable && rv.Kind() == reflect.Ptr {
285 if rv.IsNil() {
286 rv.Set(reflect.New(ft))
287 }
288 rv = rv.Elem()
289 }
290 rv.Set(conv.GoValueOf(v))
291 if isBytes && rv.Len() == 0 {
292 if nullable {
293 rv.Set(emptyBytes) // preserve presence in proto2
294 } else {
295 rv.Set(nilBytes) // do not preserve presence in proto3
296 }
297 }
298 },
299 newField: func() pref.Value {
300 return conv.New()
301 },
302 }
303}
304
305func fieldInfoForWeakMessage(fd pref.FieldDescriptor, weakOffset offset) fieldInfo {
306 if !flags.ProtoLegacy {
307 panic("no support for proto1 weak fields")
308 }
309
310 var once sync.Once
311 var messageType pref.MessageType
312 lazyInit := func() {
313 once.Do(func() {
314 messageName := fd.Message().FullName()
315 messageType, _ = preg.GlobalTypes.FindMessageByName(messageName)
316 if messageType == nil {
317 panic(fmt.Sprintf("weak message %v is not linked in", messageName))
318 }
319 })
320 }
321
322 num := fd.Number()
323 return fieldInfo{
324 fieldDesc: fd,
325 has: func(p pointer) bool {
326 if p.IsNil() {
327 return false
328 }
329 _, ok := p.Apply(weakOffset).WeakFields().get(num)
330 return ok
331 },
332 clear: func(p pointer) {
333 p.Apply(weakOffset).WeakFields().clear(num)
334 },
335 get: func(p pointer) pref.Value {
336 lazyInit()
337 if p.IsNil() {
338 return pref.ValueOfMessage(messageType.Zero())
339 }
340 m, ok := p.Apply(weakOffset).WeakFields().get(num)
341 if !ok {
342 return pref.ValueOfMessage(messageType.Zero())
343 }
344 return pref.ValueOfMessage(m.ProtoReflect())
345 },
346 set: func(p pointer, v pref.Value) {
347 lazyInit()
348 m := v.Message()
349 if m.Descriptor() != messageType.Descriptor() {
350 panic("mismatching message descriptor")
351 }
352 p.Apply(weakOffset).WeakFields().set(num, m.Interface())
353 },
354 mutable: func(p pointer) pref.Value {
355 lazyInit()
356 fs := p.Apply(weakOffset).WeakFields()
357 m, ok := fs.get(num)
358 if !ok {
359 m = messageType.New().Interface()
360 fs.set(num, m)
361 }
362 return pref.ValueOfMessage(m.ProtoReflect())
363 },
364 newMessage: func() pref.Message {
365 lazyInit()
366 return messageType.New()
367 },
368 newField: func() pref.Value {
369 lazyInit()
370 return pref.ValueOfMessage(messageType.New())
371 },
372 }
373}
374
375func fieldInfoForMessage(fd pref.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
376 ft := fs.Type
377 conv := NewConverter(ft, fd)
378
379 // TODO: Implement unsafe fast path?
380 fieldOffset := offsetOf(fs, x)
381 return fieldInfo{
382 fieldDesc: fd,
383 has: func(p pointer) bool {
384 if p.IsNil() {
385 return false
386 }
387 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
388 return !rv.IsNil()
389 },
390 clear: func(p pointer) {
391 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
392 rv.Set(reflect.Zero(rv.Type()))
393 },
394 get: func(p pointer) pref.Value {
395 if p.IsNil() {
396 return conv.Zero()
397 }
398 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
399 return conv.PBValueOf(rv)
400 },
401 set: func(p pointer, v pref.Value) {
402 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
403 rv.Set(conv.GoValueOf(v))
404 if rv.IsNil() {
405 panic("invalid nil pointer")
406 }
407 },
408 mutable: func(p pointer) pref.Value {
409 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
410 if rv.IsNil() {
411 rv.Set(conv.GoValueOf(conv.New()))
412 }
413 return conv.PBValueOf(rv)
414 },
415 newMessage: func() pref.Message {
416 return conv.New().Message()
417 },
418 newField: func() pref.Value {
419 return conv.New()
420 },
421 }
422}
423
424type oneofInfo struct {
425 oneofDesc pref.OneofDescriptor
426 which func(pointer) pref.FieldNumber
427}
428
429func makeOneofInfo(od pref.OneofDescriptor, fs reflect.StructField, x exporter, wrappersByType map[reflect.Type]pref.FieldNumber) *oneofInfo {
430 fieldOffset := offsetOf(fs, x)
431 return &oneofInfo{
432 oneofDesc: od,
433 which: func(p pointer) pref.FieldNumber {
434 if p.IsNil() {
435 return 0
436 }
437 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
438 if rv.IsNil() {
439 return 0
440 }
441 rv = rv.Elem()
442 if rv.IsNil() {
443 return 0
444 }
445 return wrappersByType[rv.Type().Elem()]
446 },
447 }
448}