message_reflect_field.go

  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}