message_opaque.go raw
1 // Copyright 2024 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
5 package impl
6
7 import (
8 "fmt"
9 "math"
10 "reflect"
11 "strings"
12 "sync/atomic"
13
14 "google.golang.org/protobuf/internal/filedesc"
15 "google.golang.org/protobuf/reflect/protoreflect"
16 )
17
18 type opaqueStructInfo struct {
19 structInfo
20 }
21
22 // isOpaque determines whether a protobuf message type is on the Opaque API. It
23 // checks whether the type is a Go struct that protoc-gen-go would generate.
24 //
25 // This function only detects newly generated messages from the v2
26 // implementation of protoc-gen-go. It is unable to classify generated messages
27 // that are too old or those that are generated by a different generator
28 // such as protoc-gen-gogo.
29 func isOpaque(t reflect.Type) bool {
30 // The current detection mechanism is to simply check the first field
31 // for a struct tag with the "protogen" key.
32 if t.Kind() == reflect.Struct && t.NumField() > 0 {
33 pgt := t.Field(0).Tag.Get("protogen")
34 return strings.HasPrefix(pgt, "opaque.")
35 }
36 return false
37 }
38
39 func opaqueInitHook(mi *MessageInfo) bool {
40 mt := mi.GoReflectType.Elem()
41 si := opaqueStructInfo{
42 structInfo: mi.makeStructInfo(mt),
43 }
44
45 if !isOpaque(mt) {
46 return false
47 }
48
49 defer atomic.StoreUint32(&mi.initDone, 1)
50
51 mi.fields = map[protoreflect.FieldNumber]*fieldInfo{}
52 fds := mi.Desc.Fields()
53 for i := 0; i < fds.Len(); i++ {
54 fd := fds.Get(i)
55 fs := si.fieldsByNumber[fd.Number()]
56 var fi fieldInfo
57 usePresence, _ := filedesc.UsePresenceForField(fd)
58
59 switch {
60 case fd.ContainingOneof() != nil && !fd.ContainingOneof().IsSynthetic():
61 // Oneofs are no different for opaque.
62 fi = fieldInfoForOneof(fd, si.oneofsByName[fd.ContainingOneof().Name()], mi.Exporter, si.oneofWrappersByNumber[fd.Number()])
63 case fd.IsMap():
64 fi = mi.fieldInfoForMapOpaque(si, fd, fs)
65 case fd.IsList() && fd.Message() == nil && usePresence:
66 fi = mi.fieldInfoForScalarListOpaque(si, fd, fs)
67 case fd.IsList() && fd.Message() == nil:
68 // Proto3 lists without presence can use same access methods as open
69 fi = fieldInfoForList(fd, fs, mi.Exporter)
70 case fd.IsList() && usePresence:
71 fi = mi.fieldInfoForMessageListOpaque(si, fd, fs)
72 case fd.IsList():
73 // Proto3 opaque messages that does not need presence bitmap.
74 // Different representation than open struct, but same logic
75 fi = mi.fieldInfoForMessageListOpaqueNoPresence(si, fd, fs)
76 case fd.Message() != nil && usePresence:
77 fi = mi.fieldInfoForMessageOpaque(si, fd, fs)
78 case fd.Message() != nil:
79 // Proto3 messages without presence can use same access methods as open
80 fi = fieldInfoForMessage(fd, fs, mi.Exporter)
81 default:
82 fi = mi.fieldInfoForScalarOpaque(si, fd, fs)
83 }
84 mi.fields[fd.Number()] = &fi
85 }
86 mi.oneofs = map[protoreflect.Name]*oneofInfo{}
87 for i := 0; i < mi.Desc.Oneofs().Len(); i++ {
88 od := mi.Desc.Oneofs().Get(i)
89 mi.oneofs[od.Name()] = makeOneofInfoOpaque(mi, od, si.structInfo, mi.Exporter)
90 }
91
92 mi.denseFields = make([]*fieldInfo, fds.Len()*2)
93 for i := 0; i < fds.Len(); i++ {
94 if fd := fds.Get(i); int(fd.Number()) < len(mi.denseFields) {
95 mi.denseFields[fd.Number()] = mi.fields[fd.Number()]
96 }
97 }
98
99 for i := 0; i < fds.Len(); {
100 fd := fds.Get(i)
101 if od := fd.ContainingOneof(); od != nil && !fd.ContainingOneof().IsSynthetic() {
102 mi.rangeInfos = append(mi.rangeInfos, mi.oneofs[od.Name()])
103 i += od.Fields().Len()
104 } else {
105 mi.rangeInfos = append(mi.rangeInfos, mi.fields[fd.Number()])
106 i++
107 }
108 }
109
110 mi.makeExtensionFieldsFunc(mt, si.structInfo)
111 mi.makeUnknownFieldsFunc(mt, si.structInfo)
112 mi.makeOpaqueCoderMethods(mt, si)
113 mi.makeFieldTypes(si.structInfo)
114
115 return true
116 }
117
118 func makeOneofInfoOpaque(mi *MessageInfo, od protoreflect.OneofDescriptor, si structInfo, x exporter) *oneofInfo {
119 oi := &oneofInfo{oneofDesc: od}
120 if od.IsSynthetic() {
121 fd := od.Fields().Get(0)
122 index, _ := presenceIndex(mi.Desc, fd)
123 oi.which = func(p pointer) protoreflect.FieldNumber {
124 if p.IsNil() {
125 return 0
126 }
127 if !mi.present(p, index) {
128 return 0
129 }
130 return od.Fields().Get(0).Number()
131 }
132 return oi
133 }
134 // Dispatch to non-opaque oneof implementation for non-synthetic oneofs.
135 return makeOneofInfo(od, si, x)
136 }
137
138 func (mi *MessageInfo) fieldInfoForMapOpaque(si opaqueStructInfo, fd protoreflect.FieldDescriptor, fs reflect.StructField) fieldInfo {
139 ft := fs.Type
140 if ft.Kind() != reflect.Map {
141 panic(fmt.Sprintf("invalid type: got %v, want map kind", ft))
142 }
143 fieldOffset := offsetOf(fs)
144 conv := NewConverter(ft, fd)
145 return fieldInfo{
146 fieldDesc: fd,
147 has: func(p pointer) bool {
148 if p.IsNil() {
149 return false
150 }
151 // Don't bother checking presence bits, since we need to
152 // look at the map length even if the presence bit is set.
153 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
154 return rv.Len() > 0
155 },
156 clear: func(p pointer) {
157 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
158 rv.Set(reflect.Zero(rv.Type()))
159 },
160 get: func(p pointer) protoreflect.Value {
161 if p.IsNil() {
162 return conv.Zero()
163 }
164 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
165 if rv.Len() == 0 {
166 return conv.Zero()
167 }
168 return conv.PBValueOf(rv)
169 },
170 set: func(p pointer, v protoreflect.Value) {
171 pv := conv.GoValueOf(v)
172 if pv.IsNil() {
173 panic(fmt.Sprintf("invalid value: setting map field to read-only value"))
174 }
175 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
176 rv.Set(pv)
177 },
178 mutable: func(p pointer) protoreflect.Value {
179 v := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
180 if v.IsNil() {
181 v.Set(reflect.MakeMap(fs.Type))
182 }
183 return conv.PBValueOf(v)
184 },
185 newField: func() protoreflect.Value {
186 return conv.New()
187 },
188 }
189 }
190
191 func (mi *MessageInfo) fieldInfoForScalarListOpaque(si opaqueStructInfo, fd protoreflect.FieldDescriptor, fs reflect.StructField) fieldInfo {
192 ft := fs.Type
193 if ft.Kind() != reflect.Slice {
194 panic(fmt.Sprintf("invalid type: got %v, want slice kind", ft))
195 }
196 conv := NewConverter(reflect.PtrTo(ft), fd)
197 fieldOffset := offsetOf(fs)
198 index, _ := presenceIndex(mi.Desc, fd)
199 return fieldInfo{
200 fieldDesc: fd,
201 has: func(p pointer) bool {
202 if p.IsNil() {
203 return false
204 }
205 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
206 return rv.Len() > 0
207 },
208 clear: func(p pointer) {
209 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
210 rv.Set(reflect.Zero(rv.Type()))
211 },
212 get: func(p pointer) protoreflect.Value {
213 if p.IsNil() {
214 return conv.Zero()
215 }
216 rv := p.Apply(fieldOffset).AsValueOf(fs.Type)
217 if rv.Elem().Len() == 0 {
218 return conv.Zero()
219 }
220 return conv.PBValueOf(rv)
221 },
222 set: func(p pointer, v protoreflect.Value) {
223 pv := conv.GoValueOf(v)
224 if pv.IsNil() {
225 panic(fmt.Sprintf("invalid value: setting repeated field to read-only value"))
226 }
227 mi.setPresent(p, index)
228 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
229 rv.Set(pv.Elem())
230 },
231 mutable: func(p pointer) protoreflect.Value {
232 mi.setPresent(p, index)
233 return conv.PBValueOf(p.Apply(fieldOffset).AsValueOf(fs.Type))
234 },
235 newField: func() protoreflect.Value {
236 return conv.New()
237 },
238 }
239 }
240
241 func (mi *MessageInfo) fieldInfoForMessageListOpaque(si opaqueStructInfo, fd protoreflect.FieldDescriptor, fs reflect.StructField) fieldInfo {
242 ft := fs.Type
243 if ft.Kind() != reflect.Ptr || ft.Elem().Kind() != reflect.Slice {
244 panic(fmt.Sprintf("invalid type: got %v, want slice kind", ft))
245 }
246 conv := NewConverter(ft, fd)
247 fieldOffset := offsetOf(fs)
248 index, _ := presenceIndex(mi.Desc, fd)
249 fieldNumber := fd.Number()
250 return fieldInfo{
251 fieldDesc: fd,
252 has: func(p pointer) bool {
253 if p.IsNil() {
254 return false
255 }
256 if !mi.present(p, index) {
257 return false
258 }
259 sp := p.Apply(fieldOffset).AtomicGetPointer()
260 if sp.IsNil() {
261 // Lazily unmarshal this field.
262 mi.lazyUnmarshal(p, fieldNumber)
263 sp = p.Apply(fieldOffset).AtomicGetPointer()
264 }
265 rv := sp.AsValueOf(fs.Type.Elem())
266 return rv.Elem().Len() > 0
267 },
268 clear: func(p pointer) {
269 fp := p.Apply(fieldOffset)
270 sp := fp.AtomicGetPointer()
271 if sp.IsNil() {
272 sp = fp.AtomicSetPointerIfNil(pointerOfValue(reflect.New(fs.Type.Elem())))
273 mi.setPresent(p, index)
274 }
275 rv := sp.AsValueOf(fs.Type.Elem())
276 rv.Elem().Set(reflect.Zero(rv.Type().Elem()))
277 },
278 get: func(p pointer) protoreflect.Value {
279 if p.IsNil() {
280 return conv.Zero()
281 }
282 if !mi.present(p, index) {
283 return conv.Zero()
284 }
285 sp := p.Apply(fieldOffset).AtomicGetPointer()
286 if sp.IsNil() {
287 // Lazily unmarshal this field.
288 mi.lazyUnmarshal(p, fieldNumber)
289 sp = p.Apply(fieldOffset).AtomicGetPointer()
290 }
291 rv := sp.AsValueOf(fs.Type.Elem())
292 if rv.Elem().Len() == 0 {
293 return conv.Zero()
294 }
295 return conv.PBValueOf(rv)
296 },
297 set: func(p pointer, v protoreflect.Value) {
298 fp := p.Apply(fieldOffset)
299 sp := fp.AtomicGetPointer()
300 if sp.IsNil() {
301 sp = fp.AtomicSetPointerIfNil(pointerOfValue(reflect.New(fs.Type.Elem())))
302 mi.setPresent(p, index)
303 }
304 rv := sp.AsValueOf(fs.Type.Elem())
305 val := conv.GoValueOf(v)
306 if val.IsNil() {
307 panic(fmt.Sprintf("invalid value: setting repeated field to read-only value"))
308 } else {
309 rv.Elem().Set(val.Elem())
310 }
311 },
312 mutable: func(p pointer) protoreflect.Value {
313 fp := p.Apply(fieldOffset)
314 sp := fp.AtomicGetPointer()
315 if sp.IsNil() {
316 if mi.present(p, index) {
317 // Lazily unmarshal this field.
318 mi.lazyUnmarshal(p, fieldNumber)
319 sp = p.Apply(fieldOffset).AtomicGetPointer()
320 } else {
321 sp = fp.AtomicSetPointerIfNil(pointerOfValue(reflect.New(fs.Type.Elem())))
322 mi.setPresent(p, index)
323 }
324 }
325 rv := sp.AsValueOf(fs.Type.Elem())
326 return conv.PBValueOf(rv)
327 },
328 newField: func() protoreflect.Value {
329 return conv.New()
330 },
331 }
332 }
333
334 func (mi *MessageInfo) fieldInfoForMessageListOpaqueNoPresence(si opaqueStructInfo, fd protoreflect.FieldDescriptor, fs reflect.StructField) fieldInfo {
335 ft := fs.Type
336 if ft.Kind() != reflect.Ptr || ft.Elem().Kind() != reflect.Slice {
337 panic(fmt.Sprintf("invalid type: got %v, want slice kind", ft))
338 }
339 conv := NewConverter(ft, fd)
340 fieldOffset := offsetOf(fs)
341 return fieldInfo{
342 fieldDesc: fd,
343 has: func(p pointer) bool {
344 if p.IsNil() {
345 return false
346 }
347 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
348 if rv.IsNil() {
349 return false
350 }
351 return rv.Elem().Len() > 0
352 },
353 clear: func(p pointer) {
354 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
355 if !rv.IsNil() {
356 rv.Elem().Set(reflect.Zero(rv.Type().Elem()))
357 }
358 },
359 get: func(p pointer) protoreflect.Value {
360 if p.IsNil() {
361 return conv.Zero()
362 }
363 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
364 if rv.IsNil() {
365 return conv.Zero()
366 }
367 if rv.Elem().Len() == 0 {
368 return conv.Zero()
369 }
370 return conv.PBValueOf(rv)
371 },
372 set: func(p pointer, v protoreflect.Value) {
373 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
374 if rv.IsNil() {
375 rv.Set(reflect.New(fs.Type.Elem()))
376 }
377 val := conv.GoValueOf(v)
378 if val.IsNil() {
379 panic(fmt.Sprintf("invalid value: setting repeated field to read-only value"))
380 } else {
381 rv.Elem().Set(val.Elem())
382 }
383 },
384 mutable: func(p pointer) protoreflect.Value {
385 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
386 if rv.IsNil() {
387 rv.Set(reflect.New(fs.Type.Elem()))
388 }
389 return conv.PBValueOf(rv)
390 },
391 newField: func() protoreflect.Value {
392 return conv.New()
393 },
394 }
395 }
396
397 func (mi *MessageInfo) fieldInfoForScalarOpaque(si opaqueStructInfo, fd protoreflect.FieldDescriptor, fs reflect.StructField) fieldInfo {
398 ft := fs.Type
399 nullable := fd.HasPresence()
400 if oneof := fd.ContainingOneof(); oneof != nil && oneof.IsSynthetic() {
401 nullable = true
402 }
403 deref := false
404 if nullable && ft.Kind() == reflect.Ptr {
405 ft = ft.Elem()
406 deref = true
407 }
408 conv := NewConverter(ft, fd)
409 fieldOffset := offsetOf(fs)
410 index, _ := presenceIndex(mi.Desc, fd)
411 var getter func(p pointer) protoreflect.Value
412 if !nullable {
413 getter = getterForDirectScalar(fd, fs, conv, fieldOffset)
414 } else {
415 getter = getterForOpaqueNullableScalar(mi, index, fd, fs, conv, fieldOffset)
416 }
417 return fieldInfo{
418 fieldDesc: fd,
419 has: func(p pointer) bool {
420 if p.IsNil() {
421 return false
422 }
423 if nullable {
424 return mi.present(p, index)
425 }
426 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
427 switch rv.Kind() {
428 case reflect.Bool:
429 return rv.Bool()
430 case reflect.Int32, reflect.Int64:
431 return rv.Int() != 0
432 case reflect.Uint32, reflect.Uint64:
433 return rv.Uint() != 0
434 case reflect.Float32, reflect.Float64:
435 return rv.Float() != 0 || math.Signbit(rv.Float())
436 case reflect.String, reflect.Slice:
437 return rv.Len() > 0
438 default:
439 panic(fmt.Sprintf("invalid type: %v", rv.Type())) // should never happen
440 }
441 },
442 clear: func(p pointer) {
443 if nullable {
444 mi.clearPresent(p, index)
445 }
446 // This is only valuable for bytes and strings, but we do it unconditionally.
447 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
448 rv.Set(reflect.Zero(rv.Type()))
449 },
450 get: getter,
451 // TODO: Implement unsafe fast path for set?
452 set: func(p pointer, v protoreflect.Value) {
453 rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
454 if deref {
455 if rv.IsNil() {
456 rv.Set(reflect.New(ft))
457 }
458 rv = rv.Elem()
459 }
460
461 rv.Set(conv.GoValueOf(v))
462 if nullable && rv.Kind() == reflect.Slice && rv.IsNil() {
463 rv.Set(emptyBytes)
464 }
465 if nullable {
466 mi.setPresent(p, index)
467 }
468 },
469 newField: func() protoreflect.Value {
470 return conv.New()
471 },
472 }
473 }
474
475 func (mi *MessageInfo) fieldInfoForMessageOpaque(si opaqueStructInfo, fd protoreflect.FieldDescriptor, fs reflect.StructField) fieldInfo {
476 ft := fs.Type
477 conv := NewConverter(ft, fd)
478 fieldOffset := offsetOf(fs)
479 index, _ := presenceIndex(mi.Desc, fd)
480 fieldNumber := fd.Number()
481 elemType := fs.Type.Elem()
482 return fieldInfo{
483 fieldDesc: fd,
484 has: func(p pointer) bool {
485 if p.IsNil() {
486 return false
487 }
488 return mi.present(p, index)
489 },
490 clear: func(p pointer) {
491 mi.clearPresent(p, index)
492 p.Apply(fieldOffset).AtomicSetNilPointer()
493 },
494 get: func(p pointer) protoreflect.Value {
495 if p.IsNil() || !mi.present(p, index) {
496 return conv.Zero()
497 }
498 fp := p.Apply(fieldOffset)
499 mp := fp.AtomicGetPointer()
500 if mp.IsNil() {
501 // Lazily unmarshal this field.
502 mi.lazyUnmarshal(p, fieldNumber)
503 mp = fp.AtomicGetPointer()
504 }
505 rv := mp.AsValueOf(elemType)
506 return conv.PBValueOf(rv)
507 },
508 set: func(p pointer, v protoreflect.Value) {
509 val := pointerOfValue(conv.GoValueOf(v))
510 if val.IsNil() {
511 panic("invalid nil pointer")
512 }
513 p.Apply(fieldOffset).AtomicSetPointer(val)
514 mi.setPresent(p, index)
515 },
516 mutable: func(p pointer) protoreflect.Value {
517 fp := p.Apply(fieldOffset)
518 mp := fp.AtomicGetPointer()
519 if mp.IsNil() {
520 if mi.present(p, index) {
521 // Lazily unmarshal this field.
522 mi.lazyUnmarshal(p, fieldNumber)
523 mp = fp.AtomicGetPointer()
524 } else {
525 mp = pointerOfValue(conv.GoValueOf(conv.New()))
526 fp.AtomicSetPointer(mp)
527 mi.setPresent(p, index)
528 }
529 }
530 return conv.PBValueOf(mp.AsValueOf(fs.Type.Elem()))
531 },
532 newMessage: func() protoreflect.Message {
533 return conv.New().Message()
534 },
535 newField: func() protoreflect.Value {
536 return conv.New()
537 },
538 }
539 }
540
541 // A presenceList wraps a List, updating presence bits as necessary when the
542 // list contents change.
543 type presenceList struct {
544 pvalueList
545 setPresence func(bool)
546 }
547 type pvalueList interface {
548 protoreflect.List
549 //Unwrapper
550 }
551
552 func (list presenceList) Append(v protoreflect.Value) {
553 list.pvalueList.Append(v)
554 list.setPresence(true)
555 }
556 func (list presenceList) Truncate(i int) {
557 list.pvalueList.Truncate(i)
558 list.setPresence(i > 0)
559 }
560
561 // presenceIndex returns the index to pass to presence functions.
562 //
563 // TODO: field.Desc.Index() would be simpler, and would give space to record the presence of oneof fields.
564 func presenceIndex(md protoreflect.MessageDescriptor, fd protoreflect.FieldDescriptor) (uint32, presenceSize) {
565 found := false
566 var index, numIndices uint32
567 for i := 0; i < md.Fields().Len(); i++ {
568 f := md.Fields().Get(i)
569 if f == fd {
570 found = true
571 index = numIndices
572 }
573 if f.ContainingOneof() == nil || isLastOneofField(f) {
574 numIndices++
575 }
576 }
577 if !found {
578 panic(fmt.Sprintf("BUG: %v not in %v", fd.Name(), md.FullName()))
579 }
580 return index, presenceSize(numIndices)
581 }
582
583 func isLastOneofField(fd protoreflect.FieldDescriptor) bool {
584 fields := fd.ContainingOneof().Fields()
585 return fields.Get(fields.Len()-1) == fd
586 }
587
588 func (mi *MessageInfo) setPresent(p pointer, index uint32) {
589 p.Apply(mi.presenceOffset).PresenceInfo().SetPresent(index, mi.presenceSize)
590 }
591
592 func (mi *MessageInfo) clearPresent(p pointer, index uint32) {
593 p.Apply(mi.presenceOffset).PresenceInfo().ClearPresent(index)
594 }
595
596 func (mi *MessageInfo) present(p pointer, index uint32) bool {
597 return p.Apply(mi.presenceOffset).PresenceInfo().Present(index)
598 }
599