xref: /aosp_15_r20/external/golang-protobuf/internal/impl/message_reflect_field.go (revision 1c12ee1efe575feb122dbf939ff15148a3b3e8f2)
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	"sync"
12
13	"google.golang.org/protobuf/internal/flags"
14	"google.golang.org/protobuf/reflect/protoreflect"
15	"google.golang.org/protobuf/reflect/protoregistry"
16)
17
18type fieldInfo struct {
19	fieldDesc protoreflect.FieldDescriptor
20
21	// These fields are used for protobuf reflection support.
22	has        func(pointer) bool
23	clear      func(pointer)
24	get        func(pointer) protoreflect.Value
25	set        func(pointer, protoreflect.Value)
26	mutable    func(pointer) protoreflect.Value
27	newMessage func() protoreflect.Message
28	newField   func() protoreflect.Value
29}
30
31func fieldInfoForMissing(fd protoreflect.FieldDescriptor) fieldInfo {
32	// This never occurs for generated message types.
33	// It implies that a hand-crafted type has missing Go fields
34	// for specific protobuf message fields.
35	return fieldInfo{
36		fieldDesc: fd,
37		has: func(p pointer) bool {
38			return false
39		},
40		clear: func(p pointer) {
41			panic("missing Go struct field for " + string(fd.FullName()))
42		},
43		get: func(p pointer) protoreflect.Value {
44			return fd.Default()
45		},
46		set: func(p pointer, v protoreflect.Value) {
47			panic("missing Go struct field for " + string(fd.FullName()))
48		},
49		mutable: func(p pointer) protoreflect.Value {
50			panic("missing Go struct field for " + string(fd.FullName()))
51		},
52		newMessage: func() protoreflect.Message {
53			panic("missing Go struct field for " + string(fd.FullName()))
54		},
55		newField: func() protoreflect.Value {
56			if v := fd.Default(); v.IsValid() {
57				return v
58			}
59			panic("missing Go struct field for " + string(fd.FullName()))
60		},
61	}
62}
63
64func fieldInfoForOneof(fd protoreflect.FieldDescriptor, fs reflect.StructField, x exporter, ot reflect.Type) fieldInfo {
65	ft := fs.Type
66	if ft.Kind() != reflect.Interface {
67		panic(fmt.Sprintf("field %v has invalid type: got %v, want interface kind", fd.FullName(), ft))
68	}
69	if ot.Kind() != reflect.Struct {
70		panic(fmt.Sprintf("field %v has invalid type: got %v, want struct kind", fd.FullName(), ot))
71	}
72	if !reflect.PtrTo(ot).Implements(ft) {
73		panic(fmt.Sprintf("field %v has invalid type: %v does not implement %v", fd.FullName(), ot, ft))
74	}
75	conv := NewConverter(ot.Field(0).Type, fd)
76	isMessage := fd.Message() != nil
77
78	// TODO: Implement unsafe fast path?
79	fieldOffset := offsetOf(fs, x)
80	return fieldInfo{
81		// NOTE: The logic below intentionally assumes that oneof fields are
82		// well-formatted. That is, the oneof interface never contains a
83		// typed nil pointer to one of the wrapper structs.
84
85		fieldDesc: fd,
86		has: func(p pointer) bool {
87			if p.IsNil() {
88				return false
89			}
90			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
91			if rv.IsNil() || rv.Elem().Type().Elem() != ot || rv.Elem().IsNil() {
92				return false
93			}
94			return true
95		},
96		clear: func(p pointer) {
97			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
98			if rv.IsNil() || rv.Elem().Type().Elem() != ot {
99				// NOTE: We intentionally don't check for rv.Elem().IsNil()
100				// so that (*OneofWrapperType)(nil) gets cleared to nil.
101				return
102			}
103			rv.Set(reflect.Zero(rv.Type()))
104		},
105		get: func(p pointer) protoreflect.Value {
106			if p.IsNil() {
107				return conv.Zero()
108			}
109			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
110			if rv.IsNil() || rv.Elem().Type().Elem() != ot || rv.Elem().IsNil() {
111				return conv.Zero()
112			}
113			rv = rv.Elem().Elem().Field(0)
114			return conv.PBValueOf(rv)
115		},
116		set: func(p pointer, v protoreflect.Value) {
117			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
118			if rv.IsNil() || rv.Elem().Type().Elem() != ot || rv.Elem().IsNil() {
119				rv.Set(reflect.New(ot))
120			}
121			rv = rv.Elem().Elem().Field(0)
122			rv.Set(conv.GoValueOf(v))
123		},
124		mutable: func(p pointer) protoreflect.Value {
125			if !isMessage {
126				panic(fmt.Sprintf("field %v with invalid Mutable call on field with non-composite type", fd.FullName()))
127			}
128			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
129			if rv.IsNil() || rv.Elem().Type().Elem() != ot || rv.Elem().IsNil() {
130				rv.Set(reflect.New(ot))
131			}
132			rv = rv.Elem().Elem().Field(0)
133			if rv.Kind() == reflect.Ptr && rv.IsNil() {
134				rv.Set(conv.GoValueOf(protoreflect.ValueOfMessage(conv.New().Message())))
135			}
136			return conv.PBValueOf(rv)
137		},
138		newMessage: func() protoreflect.Message {
139			return conv.New().Message()
140		},
141		newField: func() protoreflect.Value {
142			return conv.New()
143		},
144	}
145}
146
147func fieldInfoForMap(fd protoreflect.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
148	ft := fs.Type
149	if ft.Kind() != reflect.Map {
150		panic(fmt.Sprintf("field %v has invalid type: got %v, want map kind", fd.FullName(), ft))
151	}
152	conv := NewConverter(ft, fd)
153
154	// TODO: Implement unsafe fast path?
155	fieldOffset := offsetOf(fs, x)
156	return fieldInfo{
157		fieldDesc: fd,
158		has: func(p pointer) bool {
159			if p.IsNil() {
160				return false
161			}
162			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
163			return rv.Len() > 0
164		},
165		clear: func(p pointer) {
166			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
167			rv.Set(reflect.Zero(rv.Type()))
168		},
169		get: func(p pointer) protoreflect.Value {
170			if p.IsNil() {
171				return conv.Zero()
172			}
173			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
174			if rv.Len() == 0 {
175				return conv.Zero()
176			}
177			return conv.PBValueOf(rv)
178		},
179		set: func(p pointer, v protoreflect.Value) {
180			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
181			pv := conv.GoValueOf(v)
182			if pv.IsNil() {
183				panic(fmt.Sprintf("map field %v cannot be set with read-only value", fd.FullName()))
184			}
185			rv.Set(pv)
186		},
187		mutable: func(p pointer) protoreflect.Value {
188			v := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
189			if v.IsNil() {
190				v.Set(reflect.MakeMap(fs.Type))
191			}
192			return conv.PBValueOf(v)
193		},
194		newField: func() protoreflect.Value {
195			return conv.New()
196		},
197	}
198}
199
200func fieldInfoForList(fd protoreflect.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
201	ft := fs.Type
202	if ft.Kind() != reflect.Slice {
203		panic(fmt.Sprintf("field %v has invalid type: got %v, want slice kind", fd.FullName(), ft))
204	}
205	conv := NewConverter(reflect.PtrTo(ft), fd)
206
207	// TODO: Implement unsafe fast path?
208	fieldOffset := offsetOf(fs, x)
209	return fieldInfo{
210		fieldDesc: fd,
211		has: func(p pointer) bool {
212			if p.IsNil() {
213				return false
214			}
215			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
216			return rv.Len() > 0
217		},
218		clear: func(p pointer) {
219			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
220			rv.Set(reflect.Zero(rv.Type()))
221		},
222		get: func(p pointer) protoreflect.Value {
223			if p.IsNil() {
224				return conv.Zero()
225			}
226			rv := p.Apply(fieldOffset).AsValueOf(fs.Type)
227			if rv.Elem().Len() == 0 {
228				return conv.Zero()
229			}
230			return conv.PBValueOf(rv)
231		},
232		set: func(p pointer, v protoreflect.Value) {
233			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
234			pv := conv.GoValueOf(v)
235			if pv.IsNil() {
236				panic(fmt.Sprintf("list field %v cannot be set with read-only value", fd.FullName()))
237			}
238			rv.Set(pv.Elem())
239		},
240		mutable: func(p pointer) protoreflect.Value {
241			v := p.Apply(fieldOffset).AsValueOf(fs.Type)
242			return conv.PBValueOf(v)
243		},
244		newField: func() protoreflect.Value {
245			return conv.New()
246		},
247	}
248}
249
250var (
251	nilBytes   = reflect.ValueOf([]byte(nil))
252	emptyBytes = reflect.ValueOf([]byte{})
253)
254
255func fieldInfoForScalar(fd protoreflect.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
256	ft := fs.Type
257	nullable := fd.HasPresence()
258	isBytes := ft.Kind() == reflect.Slice && ft.Elem().Kind() == reflect.Uint8
259	if nullable {
260		if ft.Kind() != reflect.Ptr && ft.Kind() != reflect.Slice {
261			// This never occurs for generated message types.
262			// Despite the protobuf type system specifying presence,
263			// the Go field type cannot represent it.
264			nullable = false
265		}
266		if ft.Kind() == reflect.Ptr {
267			ft = ft.Elem()
268		}
269	}
270	conv := NewConverter(ft, fd)
271
272	// TODO: Implement unsafe fast path?
273	fieldOffset := offsetOf(fs, x)
274	return fieldInfo{
275		fieldDesc: fd,
276		has: func(p pointer) bool {
277			if p.IsNil() {
278				return false
279			}
280			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
281			if nullable {
282				return !rv.IsNil()
283			}
284			switch rv.Kind() {
285			case reflect.Bool:
286				return rv.Bool()
287			case reflect.Int32, reflect.Int64:
288				return rv.Int() != 0
289			case reflect.Uint32, reflect.Uint64:
290				return rv.Uint() != 0
291			case reflect.Float32, reflect.Float64:
292				return rv.Float() != 0 || math.Signbit(rv.Float())
293			case reflect.String, reflect.Slice:
294				return rv.Len() > 0
295			default:
296				panic(fmt.Sprintf("field %v has invalid type: %v", fd.FullName(), rv.Type())) // should never happen
297			}
298		},
299		clear: func(p pointer) {
300			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
301			rv.Set(reflect.Zero(rv.Type()))
302		},
303		get: func(p pointer) protoreflect.Value {
304			if p.IsNil() {
305				return conv.Zero()
306			}
307			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
308			if nullable {
309				if rv.IsNil() {
310					return conv.Zero()
311				}
312				if rv.Kind() == reflect.Ptr {
313					rv = rv.Elem()
314				}
315			}
316			return conv.PBValueOf(rv)
317		},
318		set: func(p pointer, v protoreflect.Value) {
319			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
320			if nullable && rv.Kind() == reflect.Ptr {
321				if rv.IsNil() {
322					rv.Set(reflect.New(ft))
323				}
324				rv = rv.Elem()
325			}
326			rv.Set(conv.GoValueOf(v))
327			if isBytes && rv.Len() == 0 {
328				if nullable {
329					rv.Set(emptyBytes) // preserve presence
330				} else {
331					rv.Set(nilBytes) // do not preserve presence
332				}
333			}
334		},
335		newField: func() protoreflect.Value {
336			return conv.New()
337		},
338	}
339}
340
341func fieldInfoForWeakMessage(fd protoreflect.FieldDescriptor, weakOffset offset) fieldInfo {
342	if !flags.ProtoLegacy {
343		panic("no support for proto1 weak fields")
344	}
345
346	var once sync.Once
347	var messageType protoreflect.MessageType
348	lazyInit := func() {
349		once.Do(func() {
350			messageName := fd.Message().FullName()
351			messageType, _ = protoregistry.GlobalTypes.FindMessageByName(messageName)
352			if messageType == nil {
353				panic(fmt.Sprintf("weak message %v for field %v is not linked in", messageName, fd.FullName()))
354			}
355		})
356	}
357
358	num := fd.Number()
359	return fieldInfo{
360		fieldDesc: fd,
361		has: func(p pointer) bool {
362			if p.IsNil() {
363				return false
364			}
365			_, ok := p.Apply(weakOffset).WeakFields().get(num)
366			return ok
367		},
368		clear: func(p pointer) {
369			p.Apply(weakOffset).WeakFields().clear(num)
370		},
371		get: func(p pointer) protoreflect.Value {
372			lazyInit()
373			if p.IsNil() {
374				return protoreflect.ValueOfMessage(messageType.Zero())
375			}
376			m, ok := p.Apply(weakOffset).WeakFields().get(num)
377			if !ok {
378				return protoreflect.ValueOfMessage(messageType.Zero())
379			}
380			return protoreflect.ValueOfMessage(m.ProtoReflect())
381		},
382		set: func(p pointer, v protoreflect.Value) {
383			lazyInit()
384			m := v.Message()
385			if m.Descriptor() != messageType.Descriptor() {
386				if got, want := m.Descriptor().FullName(), messageType.Descriptor().FullName(); got != want {
387					panic(fmt.Sprintf("field %v has mismatching message descriptor: got %v, want %v", fd.FullName(), got, want))
388				}
389				panic(fmt.Sprintf("field %v has mismatching message descriptor: %v", fd.FullName(), m.Descriptor().FullName()))
390			}
391			p.Apply(weakOffset).WeakFields().set(num, m.Interface())
392		},
393		mutable: func(p pointer) protoreflect.Value {
394			lazyInit()
395			fs := p.Apply(weakOffset).WeakFields()
396			m, ok := fs.get(num)
397			if !ok {
398				m = messageType.New().Interface()
399				fs.set(num, m)
400			}
401			return protoreflect.ValueOfMessage(m.ProtoReflect())
402		},
403		newMessage: func() protoreflect.Message {
404			lazyInit()
405			return messageType.New()
406		},
407		newField: func() protoreflect.Value {
408			lazyInit()
409			return protoreflect.ValueOfMessage(messageType.New())
410		},
411	}
412}
413
414func fieldInfoForMessage(fd protoreflect.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
415	ft := fs.Type
416	conv := NewConverter(ft, fd)
417
418	// TODO: Implement unsafe fast path?
419	fieldOffset := offsetOf(fs, x)
420	return fieldInfo{
421		fieldDesc: fd,
422		has: func(p pointer) bool {
423			if p.IsNil() {
424				return false
425			}
426			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
427			if fs.Type.Kind() != reflect.Ptr {
428				return !isZero(rv)
429			}
430			return !rv.IsNil()
431		},
432		clear: func(p pointer) {
433			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
434			rv.Set(reflect.Zero(rv.Type()))
435		},
436		get: func(p pointer) protoreflect.Value {
437			if p.IsNil() {
438				return conv.Zero()
439			}
440			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
441			return conv.PBValueOf(rv)
442		},
443		set: func(p pointer, v protoreflect.Value) {
444			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
445			rv.Set(conv.GoValueOf(v))
446			if fs.Type.Kind() == reflect.Ptr && rv.IsNil() {
447				panic(fmt.Sprintf("field %v has invalid nil pointer", fd.FullName()))
448			}
449		},
450		mutable: func(p pointer) protoreflect.Value {
451			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
452			if fs.Type.Kind() == reflect.Ptr && rv.IsNil() {
453				rv.Set(conv.GoValueOf(conv.New()))
454			}
455			return conv.PBValueOf(rv)
456		},
457		newMessage: func() protoreflect.Message {
458			return conv.New().Message()
459		},
460		newField: func() protoreflect.Value {
461			return conv.New()
462		},
463	}
464}
465
466type oneofInfo struct {
467	oneofDesc protoreflect.OneofDescriptor
468	which     func(pointer) protoreflect.FieldNumber
469}
470
471func makeOneofInfo(od protoreflect.OneofDescriptor, si structInfo, x exporter) *oneofInfo {
472	oi := &oneofInfo{oneofDesc: od}
473	if od.IsSynthetic() {
474		fs := si.fieldsByNumber[od.Fields().Get(0).Number()]
475		fieldOffset := offsetOf(fs, x)
476		oi.which = func(p pointer) protoreflect.FieldNumber {
477			if p.IsNil() {
478				return 0
479			}
480			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
481			if rv.IsNil() { // valid on either *T or []byte
482				return 0
483			}
484			return od.Fields().Get(0).Number()
485		}
486	} else {
487		fs := si.oneofsByName[od.Name()]
488		fieldOffset := offsetOf(fs, x)
489		oi.which = func(p pointer) protoreflect.FieldNumber {
490			if p.IsNil() {
491				return 0
492			}
493			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
494			if rv.IsNil() {
495				return 0
496			}
497			rv = rv.Elem()
498			if rv.IsNil() {
499				return 0
500			}
501			return si.oneofWrappersByType[rv.Type().Elem()]
502		}
503	}
504	return oi
505}
506
507// isZero is identical to reflect.Value.IsZero.
508// TODO: Remove this when Go1.13 is the minimally supported Go version.
509func isZero(v reflect.Value) bool {
510	switch v.Kind() {
511	case reflect.Bool:
512		return !v.Bool()
513	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
514		return v.Int() == 0
515	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
516		return v.Uint() == 0
517	case reflect.Float32, reflect.Float64:
518		return math.Float64bits(v.Float()) == 0
519	case reflect.Complex64, reflect.Complex128:
520		c := v.Complex()
521		return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0
522	case reflect.Array:
523		for i := 0; i < v.Len(); i++ {
524			if !isZero(v.Index(i)) {
525				return false
526			}
527		}
528		return true
529	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
530		return v.IsNil()
531	case reflect.String:
532		return v.Len() == 0
533	case reflect.Struct:
534		for i := 0; i < v.NumField(); i++ {
535			if !isZero(v.Field(i)) {
536				return false
537			}
538		}
539		return true
540	default:
541		panic(&reflect.ValueError{"reflect.Value.IsZero", v.Kind()})
542	}
543}
544