blob: 140f5cdb7f50d35b967a6267b020dfeffb29f8f1 [file] [log] [blame]
Joe Tsaifa02f4e2018-09-12 16:20:37 -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 "reflect"
10
Joe Tsai01ab2962018-09-21 17:44:00 -070011 "github.com/golang/protobuf/v2/internal/flags"
12 pref "github.com/golang/protobuf/v2/reflect/protoreflect"
Joe Tsaifa02f4e2018-09-12 16:20:37 -070013)
14
15type fieldInfo struct {
16 // TODO: specialize marshal and unmarshal functions?
17
18 has func(pointer) bool
19 get func(pointer) pref.Value
20 set func(pointer, pref.Value)
21 clear func(pointer)
22 mutable func(pointer) pref.Mutable
23}
24
25func fieldInfoForWeak(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo {
26 if !flags.Proto1Legacy {
27 panic("weak fields not supported")
28 }
29 // TODO: support weak fields.
30 panic(fmt.Sprintf("invalid field: %v", fd))
31}
32
33func fieldInfoForOneof(fd pref.FieldDescriptor, fs reflect.StructField, ot reflect.Type) fieldInfo {
Joe Tsai2c870bb2018-10-17 11:46:52 -070034 ft := fs.Type
35 if ft.Kind() != reflect.Interface {
36 panic(fmt.Sprintf("invalid type: got %v, want interface kind", ft))
37 }
38 if ot.Kind() != reflect.Struct {
39 panic(fmt.Sprintf("invalid type: got %v, want struct kind", ot))
40 }
41 if !reflect.PtrTo(ot).Implements(ft) {
42 panic(fmt.Sprintf("invalid type: %v does not implement %v", ot, ft))
43 }
44 conv := matchGoTypePBKind(ot.Field(0).Type, fd.Kind())
45 fieldOffset := offsetOf(fs)
46 // TODO: Implement unsafe fast path?
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 has: func(p pointer) bool {
53 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
54 if rv.IsNil() || rv.Elem().Type().Elem() != ot {
55 return false
56 }
57 return true
58 },
59 get: func(p pointer) pref.Value {
60 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
61 if rv.IsNil() || rv.Elem().Type().Elem() != ot {
62 if fd.Kind() == pref.MessageKind || fd.Kind() == pref.GroupKind {
Joe Tsaice6edd32018-10-19 16:27:46 -070063 // TODO: Should this return an invalid protoreflect.Value?
Joe Tsai2c870bb2018-10-17 11:46:52 -070064 rv = reflect.Zero(ot.Field(0).Type)
65 return conv.toPB(rv)
66 }
67 return fd.Default()
68 }
69 rv = rv.Elem().Elem().Field(0)
70 return conv.toPB(rv)
71 },
72 set: func(p pointer, v pref.Value) {
73 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
74 if rv.IsNil() || rv.Elem().Type().Elem() != ot {
75 rv.Set(reflect.New(ot))
76 }
77 rv = rv.Elem().Elem().Field(0)
78 rv.Set(conv.toGo(v))
79 },
80 clear: func(p pointer) {
81 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
82 if rv.IsNil() || rv.Elem().Type().Elem() != ot {
83 return
84 }
85 rv.Set(reflect.Zero(rv.Type()))
86 },
87 mutable: func(p pointer) pref.Mutable {
88 // Mutable is only valid for messages and panics for other kinds.
89 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
90 if rv.IsNil() || rv.Elem().Type().Elem() != ot {
91 rv.Set(reflect.New(ot))
92 }
93 rv = rv.Elem().Elem().Field(0)
94 if rv.IsNil() {
95 pv := pref.ValueOf(conv.newMessage())
96 rv.Set(conv.toGo(pv))
97 }
98 return rv.Interface().(pref.Message)
99 },
100 }
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700101}
102
103func fieldInfoForMap(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo {
Joe Tsaibbfaeb72018-10-17 00:27:21 +0000104 ft := fs.Type
105 if ft.Kind() != reflect.Map {
106 panic(fmt.Sprintf("invalid type: got %v, want map kind", ft))
107 }
108 keyConv := matchGoTypePBKind(ft.Key(), fd.MessageType().Fields().ByNumber(1).Kind())
109 valConv := matchGoTypePBKind(ft.Elem(), fd.MessageType().Fields().ByNumber(2).Kind())
110 fieldOffset := offsetOf(fs)
111 // TODO: Implement unsafe fast path?
112 return fieldInfo{
113 has: func(p pointer) bool {
114 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
115 return rv.Len() > 0
116 },
117 get: func(p pointer) pref.Value {
118 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
119 return pref.ValueOf(mapReflect{rv, keyConv, valConv})
120 },
121 set: func(p pointer, v pref.Value) {
122 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
123 rv.Set(v.Map().(mapReflect).v)
124 },
125 clear: func(p pointer) {
126 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
127 rv.Set(reflect.Zero(rv.Type()))
128 },
129 mutable: func(p pointer) pref.Mutable {
130 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
131 return mapReflect{rv, keyConv, valConv}
132 },
133 }
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700134}
135
Joe Tsaibbfaeb72018-10-17 00:27:21 +0000136type mapReflect struct {
137 v reflect.Value // addressable map[K]V
138 keyConv converter
139 valConv converter
140}
141
Joe Tsaibbfaeb72018-10-17 00:27:21 +0000142func (ms mapReflect) Len() int {
143 return ms.v.Len()
144}
145func (ms mapReflect) Has(k pref.MapKey) bool {
146 rk := ms.keyConv.toGo(k.Value())
147 rv := ms.v.MapIndex(rk)
148 return rv.IsValid()
149}
150func (ms mapReflect) Get(k pref.MapKey) pref.Value {
151 rk := ms.keyConv.toGo(k.Value())
152 rv := ms.v.MapIndex(rk)
153 if !rv.IsValid() {
154 return pref.Value{}
155 }
156 return ms.valConv.toPB(rv)
157}
158func (ms mapReflect) Set(k pref.MapKey, v pref.Value) {
159 if ms.v.IsNil() {
160 ms.v.Set(reflect.MakeMap(ms.v.Type()))
161 }
162 rk := ms.keyConv.toGo(k.Value())
163 rv := ms.valConv.toGo(v)
164 ms.v.SetMapIndex(rk, rv)
165}
166func (ms mapReflect) Clear(k pref.MapKey) {
167 rk := ms.keyConv.toGo(k.Value())
168 ms.v.SetMapIndex(rk, reflect.Value{})
169}
170func (ms mapReflect) Mutable(k pref.MapKey) pref.Mutable {
171 // Mutable is only valid for messages and panics for other kinds.
172 if ms.v.IsNil() {
173 ms.v.Set(reflect.MakeMap(ms.v.Type()))
174 }
175 rk := ms.keyConv.toGo(k.Value())
176 rv := ms.v.MapIndex(rk)
177 if !rv.IsValid() || rv.IsNil() {
178 pv := pref.ValueOf(ms.valConv.newMessage())
179 rv = ms.valConv.toGo(pv)
180 ms.v.SetMapIndex(rk, rv)
181 }
182 return rv.Interface().(pref.Message)
183}
184func (ms mapReflect) Range(f func(pref.MapKey, pref.Value) bool) {
185 for _, k := range ms.v.MapKeys() {
186 if v := ms.v.MapIndex(k); v.IsValid() {
187 pk := ms.keyConv.toPB(k).MapKey()
188 pv := ms.valConv.toPB(v)
189 if !f(pk, pv) {
190 return
191 }
192 }
193 }
194}
195func (ms mapReflect) Unwrap() interface{} { // TODO: unexport?
196 return ms.v.Interface()
197}
198func (ms mapReflect) ProtoMutable() {}
199
Joe Tsaice6edd32018-10-19 16:27:46 -0700200var _ pref.Map = mapReflect{}
201
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700202func fieldInfoForVector(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo {
Joe Tsai91e14662018-09-13 13:24:35 -0700203 ft := fs.Type
204 if ft.Kind() != reflect.Slice {
205 panic(fmt.Sprintf("invalid type: got %v, want slice kind", ft))
206 }
207 conv := matchGoTypePBKind(ft.Elem(), fd.Kind())
208 fieldOffset := offsetOf(fs)
209 // TODO: Implement unsafe fast path?
210 return fieldInfo{
211 has: func(p pointer) bool {
212 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
213 return rv.Len() > 0
214 },
215 get: func(p pointer) pref.Value {
216 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
217 return pref.ValueOf(vectorReflect{rv, conv})
218 },
219 set: func(p pointer, v pref.Value) {
220 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
221 rv.Set(v.Vector().(vectorReflect).v)
222 },
223 clear: func(p pointer) {
224 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
225 rv.Set(reflect.Zero(rv.Type()))
226 },
227 mutable: func(p pointer) pref.Mutable {
228 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
229 return vectorReflect{rv, conv}
230 },
231 }
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700232}
233
Joe Tsai91e14662018-09-13 13:24:35 -0700234type vectorReflect struct {
235 v reflect.Value // addressable []T
236 conv converter
237}
238
239func (vs vectorReflect) Len() int {
240 return vs.v.Len()
241}
242func (vs vectorReflect) Get(i int) pref.Value {
243 return vs.conv.toPB(vs.v.Index(i))
244}
245func (vs vectorReflect) Set(i int, v pref.Value) {
246 vs.v.Index(i).Set(vs.conv.toGo(v))
247}
248func (vs vectorReflect) Append(v pref.Value) {
249 vs.v.Set(reflect.Append(vs.v, vs.conv.toGo(v)))
250}
251func (vs vectorReflect) Mutable(i int) pref.Mutable {
252 // Mutable is only valid for messages and panics for other kinds.
253 rv := vs.v.Index(i)
254 if rv.IsNil() {
255 pv := pref.ValueOf(vs.conv.newMessage())
256 rv.Set(vs.conv.toGo(pv))
257 }
258 return rv.Interface().(pref.Message)
259}
260func (vs vectorReflect) MutableAppend() pref.Mutable {
261 // MutableAppend is only valid for messages and panics for other kinds.
262 pv := pref.ValueOf(vs.conv.newMessage())
263 vs.v.Set(reflect.Append(vs.v, vs.conv.toGo(pv)))
264 return vs.v.Index(vs.Len() - 1).Interface().(pref.Message)
265}
266func (vs vectorReflect) Truncate(i int) {
267 vs.v.Set(vs.v.Slice(0, i))
268}
269func (vs vectorReflect) Unwrap() interface{} { // TODO: unexport?
270 return vs.v.Interface()
271}
272func (vs vectorReflect) ProtoMutable() {}
273
274var _ pref.Vector = vectorReflect{}
275
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700276var emptyBytes = reflect.ValueOf([]byte{})
277
278func fieldInfoForScalar(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo {
279 ft := fs.Type
280 nullable := fd.Syntax() == pref.Proto2
281 if nullable {
282 if ft.Kind() != reflect.Ptr && ft.Kind() != reflect.Slice {
283 panic(fmt.Sprintf("invalid type: got %v, want pointer", ft))
284 }
285 if ft.Kind() == reflect.Ptr {
286 ft = ft.Elem()
287 }
288 }
289 conv := matchGoTypePBKind(ft, fd.Kind())
290 fieldOffset := offsetOf(fs)
291 // TODO: Implement unsafe fast path?
292 return fieldInfo{
293 has: func(p pointer) bool {
294 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
295 if nullable {
296 return !rv.IsNil()
297 }
298 switch rv.Kind() {
299 case reflect.Bool:
300 return rv.Bool()
301 case reflect.Int32, reflect.Int64:
302 return rv.Int() > 0
303 case reflect.Uint32, reflect.Uint64:
304 return rv.Uint() > 0
305 case reflect.Float32, reflect.Float64:
306 return rv.Float() > 0
307 case reflect.String, reflect.Slice:
308 return rv.Len() > 0
309 default:
310 panic(fmt.Sprintf("invalid type: %v", rv.Type())) // should never happen
311 }
312 },
313 get: func(p pointer) pref.Value {
314 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
315 if nullable {
316 if rv.IsNil() {
317 pv := fd.Default()
318 if fd.Kind() == pref.BytesKind && len(pv.Bytes()) > 0 {
319 return pref.ValueOf(append([]byte(nil), pv.Bytes()...)) // copy default bytes for safety
320 }
321 return pv
322 }
323 if rv.Kind() == reflect.Ptr {
324 rv = rv.Elem()
325 }
326 }
327 return conv.toPB(rv)
328 },
329 set: func(p pointer, v pref.Value) {
330 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
331 if nullable && rv.Kind() == reflect.Ptr {
332 if rv.IsNil() {
333 rv.Set(reflect.New(ft))
334 }
335 rv = rv.Elem()
336 }
337 rv.Set(conv.toGo(v))
338 if nullable && rv.Kind() == reflect.Slice && rv.IsNil() {
339 rv.Set(emptyBytes)
340 }
341 },
342 clear: func(p pointer) {
343 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
344 rv.Set(reflect.Zero(rv.Type()))
345 },
346 mutable: func(p pointer) pref.Mutable {
347 panic("invalid mutable call")
348 },
349 }
350}
351
352func fieldInfoForMessage(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo {
Joe Tsaice6edd32018-10-19 16:27:46 -0700353 ft := fs.Type
354 conv := matchGoTypePBKind(ft, fd.Kind())
355 fieldOffset := offsetOf(fs)
356 // TODO: Implement unsafe fast path?
357 return fieldInfo{
358 has: func(p pointer) bool {
359 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
360 return !rv.IsNil()
361 },
362 get: func(p pointer) pref.Value {
363 // TODO: If rv.IsNil(), should this return a typed-nil pointer or
364 // an invalid protoreflect.Value?
365 //
366 // Returning a typed nil pointer assumes that such values
367 // are valid for all possible custom message types,
368 // which may not be case for dynamic messages.
369 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
370 return conv.toPB(rv)
371 },
372 set: func(p pointer, v pref.Value) {
373 // TODO: Similarly, is it valid to set this to a typed nil pointer?
374 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
375 rv.Set(conv.toGo(v))
376 },
377 clear: func(p pointer) {
378 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
379 rv.Set(reflect.Zero(rv.Type()))
380 },
381 mutable: func(p pointer) pref.Mutable {
382 // Mutable is only valid for messages and panics for other kinds.
383 rv := p.apply(fieldOffset).asType(fs.Type).Elem()
384 if rv.IsNil() {
385 pv := pref.ValueOf(conv.newMessage())
386 rv.Set(conv.toGo(pv))
387 }
388 return conv.toPB(rv).Message()
389 },
390 }
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700391}
392
393// messageV1 is the protoV1.Message interface.
394type messageV1 interface {
395 Reset()
396 String() string
397 ProtoMessage()
398}
399
400var (
401 boolType = reflect.TypeOf(bool(false))
402 int32Type = reflect.TypeOf(int32(0))
403 int64Type = reflect.TypeOf(int64(0))
404 uint32Type = reflect.TypeOf(uint32(0))
405 uint64Type = reflect.TypeOf(uint64(0))
406 float32Type = reflect.TypeOf(float32(0))
407 float64Type = reflect.TypeOf(float64(0))
408 stringType = reflect.TypeOf(string(""))
409 bytesType = reflect.TypeOf([]byte(nil))
410
411 enumIfaceV2 = reflect.TypeOf((*pref.ProtoEnum)(nil)).Elem()
412 messageIfaceV1 = reflect.TypeOf((*messageV1)(nil)).Elem()
413 messageIfaceV2 = reflect.TypeOf((*pref.ProtoMessage)(nil)).Elem()
414
415 byteType = reflect.TypeOf(byte(0))
416)
417
418// matchGoTypePBKind matches a Go type with the protobuf kind.
419//
420// This matcher deliberately supports a wider range of Go types than what
421// protoc-gen-go historically generated to be able to automatically wrap some
422// v1 messages generated by other forks of protoc-gen-go.
423func matchGoTypePBKind(t reflect.Type, k pref.Kind) converter {
424 switch k {
425 case pref.BoolKind:
426 if t.Kind() == reflect.Bool {
427 return makeScalarConverter(t, boolType)
428 }
429 case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
430 if t.Kind() == reflect.Int32 {
431 return makeScalarConverter(t, int32Type)
432 }
433 case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
434 if t.Kind() == reflect.Int64 {
435 return makeScalarConverter(t, int64Type)
436 }
437 case pref.Uint32Kind, pref.Fixed32Kind:
438 if t.Kind() == reflect.Uint32 {
439 return makeScalarConverter(t, uint32Type)
440 }
441 case pref.Uint64Kind, pref.Fixed64Kind:
442 if t.Kind() == reflect.Uint64 {
443 return makeScalarConverter(t, uint64Type)
444 }
445 case pref.FloatKind:
446 if t.Kind() == reflect.Float32 {
447 return makeScalarConverter(t, float32Type)
448 }
449 case pref.DoubleKind:
450 if t.Kind() == reflect.Float64 {
451 return makeScalarConverter(t, float64Type)
452 }
453 case pref.StringKind:
454 if t.Kind() == reflect.String || (t.Kind() == reflect.Slice && t.Elem() == byteType) {
455 return makeScalarConverter(t, stringType)
456 }
457 case pref.BytesKind:
458 if t.Kind() == reflect.String || (t.Kind() == reflect.Slice && t.Elem() == byteType) {
459 return makeScalarConverter(t, bytesType)
460 }
461 case pref.EnumKind:
462 // Handle v2 enums, which must satisfy the proto.Enum interface.
463 if t.Kind() != reflect.Ptr && t.Implements(enumIfaceV2) {
Joe Tsaice6edd32018-10-19 16:27:46 -0700464 et := reflect.Zero(t).Interface().(pref.ProtoEnum).ProtoReflect().Type()
465 return converter{
466 toPB: func(v reflect.Value) pref.Value {
467 if v.Type() != t {
468 panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
469 }
470 e := v.Interface().(pref.ProtoEnum)
471 return pref.ValueOf(e.ProtoReflect().Number())
472 },
473 toGo: func(v pref.Value) reflect.Value {
474 rv := reflect.ValueOf(et.GoNew(v.Enum()))
475 if rv.Type() != t {
476 panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), t))
477 }
478 return rv
479 },
480 }
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700481 }
482
483 // Handle v1 enums, which we identify as simply a named int32 type.
484 if t.Kind() == reflect.Int32 && t.PkgPath() != "" {
Joe Tsaice6edd32018-10-19 16:27:46 -0700485 return converter{
486 toPB: func(v reflect.Value) pref.Value {
487 if v.Type() != t {
488 panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
489 }
490 return pref.ValueOf(pref.EnumNumber(v.Int()))
491 },
492 toGo: func(v pref.Value) reflect.Value {
493 return reflect.ValueOf(v.Enum()).Convert(t)
494 },
495 }
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700496 }
497 case pref.MessageKind, pref.GroupKind:
498 // Handle v2 messages, which must satisfy the proto.Message interface.
499 if t.Kind() == reflect.Ptr && t.Implements(messageIfaceV2) {
Joe Tsaice6edd32018-10-19 16:27:46 -0700500 mt := reflect.Zero(t).Interface().(pref.ProtoMessage).ProtoReflect().Type()
501 return converter{
502 toPB: func(v reflect.Value) pref.Value {
503 if v.Type() != t {
504 panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
505 }
506 return pref.ValueOf(v.Interface())
507 },
508 toGo: func(v pref.Value) reflect.Value {
509 rv := reflect.ValueOf(v.Message())
510 if rv.Type() != t {
511 panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), t))
512 }
513 return rv
514 },
515 newMessage: func() pref.Message {
516 return mt.GoNew().ProtoReflect()
517 },
518 }
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700519 }
520
521 // Handle v1 messages, which we need to wrap as a v2 message.
522 if t.Kind() == reflect.Ptr && t.Implements(messageIfaceV1) {
Joe Tsaice6edd32018-10-19 16:27:46 -0700523 return converter{
524 toPB: func(v reflect.Value) pref.Value {
525 if v.Type() != t {
526 panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
527 }
528 return pref.ValueOf(wrapLegacyMessage(v))
529 },
530 toGo: func(v pref.Value) reflect.Value {
531 type unwrapper interface{ Unwrap() interface{} }
532 rv := reflect.ValueOf(v.Message().(unwrapper).Unwrap())
533 if rv.Type() != t {
534 panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), t))
535 }
536 return rv
537 },
538 newMessage: func() pref.Message {
539 return wrapLegacyMessage(reflect.New(t.Elem()))
540 },
541 }
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700542 }
543 }
544 panic(fmt.Sprintf("invalid Go type %v for protobuf kind %v", t, k))
545}
546
547// converter provides functions for converting to/from Go reflect.Value types
548// and protobuf protoreflect.Value types.
549type converter struct {
Joe Tsai91e14662018-09-13 13:24:35 -0700550 toPB func(reflect.Value) pref.Value
551 toGo func(pref.Value) reflect.Value
552 newMessage func() pref.Message
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700553}
554
555func makeScalarConverter(goType, pbType reflect.Type) converter {
556 return converter{
557 toPB: func(v reflect.Value) pref.Value {
558 if v.Type() != goType {
559 panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), goType))
560 }
561 if goType.Kind() == reflect.String && pbType.Kind() == reflect.Slice && v.Len() == 0 {
562 return pref.ValueOf([]byte(nil)) // ensure empty string is []byte(nil)
563 }
564 return pref.ValueOf(v.Convert(pbType).Interface())
565 },
566 toGo: func(v pref.Value) reflect.Value {
567 rv := reflect.ValueOf(v.Interface())
568 if rv.Type() != pbType {
569 panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), pbType))
570 }
571 if pbType.Kind() == reflect.String && goType.Kind() == reflect.Slice && rv.Len() == 0 {
572 return reflect.Zero(goType) // ensure empty string is []byte(nil)
573 }
574 return rv.Convert(goType)
575 },
576 }
577}