message_reflect_field.go raw

   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  
   5  package impl
   6  
   7  import (
   8  	"fmt"
   9  	"math"
  10  	"reflect"
  11  
  12  	"google.golang.org/protobuf/reflect/protoreflect"
  13  )
  14  
  15  type 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  
  28  func 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  
  61  func 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  
 144  func 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  
 197  func 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  
 247  var (
 248  	nilBytes   = reflect.ValueOf([]byte(nil))
 249  	emptyBytes = reflect.ValueOf([]byte{})
 250  )
 251  
 252  func 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  
 332  func 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  
 384  type oneofInfo struct {
 385  	oneofDesc protoreflect.OneofDescriptor
 386  	which     func(pointer) protoreflect.FieldNumber
 387  }
 388  
 389  func 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  }
 424