all: refactor Converter

A Converter converts between reflect.Values and protoreflect.Values.
The existing usage of Converter is somewhat confusing: The
internal/value package creates Converters for scalar types only, the
internal/impl package creates Converters for legacy messages and enums,
and the reflect/prototype package creates Converters for repeated fields.

Change the Converter type to an interface. The constructor for
Converter takes a FieldDescriptor and reflect.Type, and directly
handles conversions for all field types: Scalars, lists, maps, and
legacy types.

Move Converter into the internal/impl package, since that package
contains the necessary support for dealing with legacy messages and
enums. Drop the internal/value package.

Replace two uses of prototype.Extension with more focused
implementations, since the implementation is trivial with the
refactored Converter. Drop prototype.Extension for the moment since
it is now unused.

Change-Id: If0c570fefac002cc5925b3d56281b6eb17e90d5f
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/187857
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
This commit is contained in:
Damien Neil 2019-07-17 16:52:10 -07:00
parent f542220747
commit 954bd92c19
12 changed files with 497 additions and 720 deletions

View File

@ -7,8 +7,11 @@
package filetype
import (
"fmt"
"reflect"
"sync"
"google.golang.org/protobuf/internal/descfmt"
"google.golang.org/protobuf/internal/descopts"
fdesc "google.golang.org/protobuf/internal/filedesc"
pimpl "google.golang.org/protobuf/internal/impl"
@ -223,24 +226,26 @@ func (tb TypeBuilder) Build() (out struct {
var depIdx int32
out.Extensions = make([]Extension, len(fbOut.Extensions))
for i := range fbOut.Extensions {
out.Extensions[i] = Extension{Extension: ptype.Extension{
ExtensionDescriptor: &fbOut.Extensions[i],
}}
// For enum and message kinds, determine the referent Go type so
// that we can construct their constructors.
const listExtDeps = 2
var goType reflect.Type
switch fbOut.Extensions[i].L1.Kind {
case pref.EnumKind:
j := depIdxs.Get(tb.DependencyIndexes, listExtDeps, depIdx)
goType := reflect.TypeOf(tb.GoTypes[j])
out.Extensions[i].NewEnum = enumMaker(goType)
goType = reflect.TypeOf(tb.GoTypes[j])
depIdx++
case pref.MessageKind, pref.GroupKind:
j := depIdxs.Get(tb.DependencyIndexes, listExtDeps, depIdx)
goType := reflect.TypeOf(tb.GoTypes[j])
out.Extensions[i].NewMessage = messageMaker(goType)
goType = reflect.TypeOf(tb.GoTypes[j])
depIdx++
default:
goType = goTypeForPBKind[fbOut.Extensions[i].L1.Kind]
}
out.Extensions[i] = Extension{
ExtensionDescriptor: &fbOut.Extensions[i],
goType: goType,
}
// Keep v1 and v2 extensions in sync.
@ -259,6 +264,24 @@ func (tb TypeBuilder) Build() (out struct {
return out
}
var goTypeForPBKind = map[pref.Kind]reflect.Type{
pref.BoolKind: reflect.TypeOf(bool(false)),
pref.Int32Kind: reflect.TypeOf(int32(0)),
pref.Sint32Kind: reflect.TypeOf(int32(0)),
pref.Sfixed32Kind: reflect.TypeOf(int32(0)),
pref.Int64Kind: reflect.TypeOf(int64(0)),
pref.Sint64Kind: reflect.TypeOf(int64(0)),
pref.Sfixed64Kind: reflect.TypeOf(int64(0)),
pref.Uint32Kind: reflect.TypeOf(uint32(0)),
pref.Fixed32Kind: reflect.TypeOf(uint32(0)),
pref.Uint64Kind: reflect.TypeOf(uint64(0)),
pref.Fixed64Kind: reflect.TypeOf(uint64(0)),
pref.FloatKind: reflect.TypeOf(float32(0)),
pref.DoubleKind: reflect.TypeOf(float64(0)),
pref.StringKind: reflect.TypeOf(string("")),
pref.BytesKind: reflect.TypeOf([]byte(nil)),
}
type depIdxs []int32
// Get retrieves the jth element of the ith sub-list.
@ -310,14 +333,33 @@ func messageMaker(t reflect.Type) func() pref.Message {
}
type (
Enum = ptype.Enum
Message = ptype.Message
Extension struct {
ptype.Extension
legacyDesc *piface.ExtensionDescV1
}
Enum = ptype.Enum
Message = ptype.Message
)
type Extension struct {
pref.ExtensionDescriptor
legacyDesc *piface.ExtensionDescV1
once sync.Once
goType reflect.Type
conv pimpl.Converter
}
func (t *Extension) New() pref.Value { return t.lazyInit().New() }
func (t *Extension) ValueOf(v interface{}) pref.Value {
return t.lazyInit().PBValueOf(reflect.ValueOf(v))
}
func (t *Extension) InterfaceOf(v pref.Value) interface{} {
return t.lazyInit().GoValueOf(v).Interface()
}
func (t *Extension) GoType() reflect.Type {
t.lazyInit()
return t.goType
}
func (t *Extension) Descriptor() pref.ExtensionDescriptor { return t.ExtensionDescriptor }
func (t *Extension) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, t) }
// ProtoLegacyExtensionDesc is a pseudo-internal API for allowing the v1 code
// to be able to retrieve a v1 ExtensionDesc.
//
@ -326,3 +368,13 @@ type (
func (x *Extension) ProtoLegacyExtensionDesc() *piface.ExtensionDescV1 {
return x.legacyDesc
}
func (t *Extension) lazyInit() pimpl.Converter {
t.once.Do(func() {
if t.ExtensionDescriptor.Cardinality() == pref.Repeated {
t.goType = reflect.PtrTo(reflect.SliceOf(t.goType))
}
t.conv = pimpl.NewConverter(t.goType, t.ExtensionDescriptor)
})
return t.conv
}

197
internal/impl/convert.go Normal file
View File

@ -0,0 +1,197 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package impl
import (
"fmt"
"reflect"
pref "google.golang.org/protobuf/reflect/protoreflect"
)
// Unwrapper unwraps the value to the underlying value.
// This is implemented by List and Map.
type Unwrapper interface {
ProtoUnwrap() interface{}
}
// A Converter coverts to/from Go reflect.Value types and protobuf protoreflect.Value types.
type Converter interface {
PBValueOf(reflect.Value) pref.Value
GoValueOf(pref.Value) reflect.Value
New() pref.Value
}
// NewConverter matches a Go type with a protobuf field and returns a Converter
// that converts between the two. Enums must be a named int32 kind that
// implements protoreflect.Enum, and messages must be pointer to a named
// struct type that implements protoreflect.ProtoMessage.
//
// This matcher deliberately supports a wider range of Go types than what
// protoc-gen-go historically generated to be able to automatically wrap some
// v1 messages generated by other forks of protoc-gen-go.
func NewConverter(t reflect.Type, fd pref.FieldDescriptor) Converter {
switch {
case fd.IsList():
return newListConverter(t, fd)
case fd.IsMap():
return newMapConverter(t, fd)
default:
return newSingularConverter(t, fd)
}
panic(fmt.Sprintf("invalid Go type %v for field %v", t, fd.FullName()))
}
var (
boolType = reflect.TypeOf(bool(false))
int32Type = reflect.TypeOf(int32(0))
int64Type = reflect.TypeOf(int64(0))
uint32Type = reflect.TypeOf(uint32(0))
uint64Type = reflect.TypeOf(uint64(0))
float32Type = reflect.TypeOf(float32(0))
float64Type = reflect.TypeOf(float64(0))
stringType = reflect.TypeOf(string(""))
bytesType = reflect.TypeOf([]byte(nil))
byteType = reflect.TypeOf(byte(0))
)
type scalarConverter struct {
goType, pbType reflect.Type
def pref.Value
}
func newSingularConverter(t reflect.Type, fd pref.FieldDescriptor) Converter {
switch fd.Kind() {
case pref.BoolKind:
if t.Kind() == reflect.Bool {
return &scalarConverter{t, boolType, fd.Default()}
}
case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
if t.Kind() == reflect.Int32 {
return &scalarConverter{t, int32Type, fd.Default()}
}
case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
if t.Kind() == reflect.Int64 {
return &scalarConverter{t, int64Type, fd.Default()}
}
case pref.Uint32Kind, pref.Fixed32Kind:
if t.Kind() == reflect.Uint32 {
return &scalarConverter{t, uint32Type, fd.Default()}
}
case pref.Uint64Kind, pref.Fixed64Kind:
if t.Kind() == reflect.Uint64 {
return &scalarConverter{t, uint64Type, fd.Default()}
}
case pref.FloatKind:
if t.Kind() == reflect.Float32 {
return &scalarConverter{t, float32Type, fd.Default()}
}
case pref.DoubleKind:
if t.Kind() == reflect.Float64 {
return &scalarConverter{t, float64Type, fd.Default()}
}
case pref.StringKind:
if t.Kind() == reflect.String || (t.Kind() == reflect.Slice && t.Elem() == byteType) {
return &scalarConverter{t, stringType, fd.Default()}
}
case pref.BytesKind:
if t.Kind() == reflect.String || (t.Kind() == reflect.Slice && t.Elem() == byteType) {
return &scalarConverter{t, bytesType, fd.Default()}
}
case pref.EnumKind:
// Handle enums, which must be a named int32 type.
if t.Kind() == reflect.Int32 {
return newEnumConverter(t, fd)
}
case pref.MessageKind, pref.GroupKind:
return newMessageConverter(t)
}
panic(fmt.Sprintf("invalid Go type %v for field %v", t, fd.FullName()))
}
func (c *scalarConverter) PBValueOf(v reflect.Value) pref.Value {
if v.Type() != c.goType {
panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), c.goType))
}
if c.goType.Kind() == reflect.String && c.pbType.Kind() == reflect.Slice && v.Len() == 0 {
return pref.ValueOf([]byte(nil)) // ensure empty string is []byte(nil)
}
return pref.ValueOf(v.Convert(c.pbType).Interface())
}
func (c *scalarConverter) GoValueOf(v pref.Value) reflect.Value {
rv := reflect.ValueOf(v.Interface())
if rv.Type() != c.pbType {
panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), c.pbType))
}
if c.pbType.Kind() == reflect.String && c.goType.Kind() == reflect.Slice && rv.Len() == 0 {
return reflect.Zero(c.goType) // ensure empty string is []byte(nil)
}
return rv.Convert(c.goType)
}
func (c *scalarConverter) New() pref.Value {
return c.def
}
type enumConverter struct {
goType reflect.Type
def pref.Value
}
func newEnumConverter(goType reflect.Type, fd pref.FieldDescriptor) Converter {
return &enumConverter{goType, fd.Default()}
}
func (c *enumConverter) PBValueOf(v reflect.Value) pref.Value {
if v.Type() != c.goType {
panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), c.goType))
}
return pref.ValueOf(pref.EnumNumber(v.Int()))
}
func (c *enumConverter) GoValueOf(v pref.Value) reflect.Value {
return reflect.ValueOf(v.Enum()).Convert(c.goType)
}
func (c *enumConverter) New() pref.Value {
return c.def
}
type messageConverter struct {
goType reflect.Type
}
func newMessageConverter(goType reflect.Type) Converter {
return &messageConverter{goType}
}
func (c *messageConverter) PBValueOf(v reflect.Value) pref.Value {
if v.Type() != c.goType {
panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), c.goType))
}
if m, ok := v.Interface().(pref.ProtoMessage); ok {
return pref.ValueOf(m.ProtoReflect())
}
return pref.ValueOf(legacyWrapMessage(v).ProtoReflect())
}
func (c *messageConverter) GoValueOf(v pref.Value) reflect.Value {
m := v.Message()
var rv reflect.Value
if u, ok := m.(Unwrapper); ok {
rv = reflect.ValueOf(u.ProtoUnwrap())
} else {
rv = reflect.ValueOf(m.Interface())
}
if rv.Type() != c.goType {
panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), c.goType))
}
return rv
}
func (c *messageConverter) New() pref.Value {
return c.PBValueOf(reflect.New(c.goType.Elem()))
}

View File

@ -2,23 +2,40 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package value
package impl
import (
"fmt"
"reflect"
pref "google.golang.org/protobuf/reflect/protoreflect"
)
// ListOf returns a protoreflect.List view of p, which must be a *[]T.
// If p is nil, this returns an empty, read-only list.
func ListOf(p interface{}, c Converter) interface {
pref.List
Unwrapper
} {
// TODO: Validate that p is a *[]T?
rv := reflect.ValueOf(p)
return &listReflect{rv, c}
type listConverter struct {
goType reflect.Type
c Converter
}
func newListConverter(t reflect.Type, fd pref.FieldDescriptor) Converter {
if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Slice {
panic(fmt.Sprintf("invalid Go type %v for field %v", t, fd.FullName()))
}
return &listConverter{t, newSingularConverter(t.Elem().Elem(), fd)}
}
func (c *listConverter) PBValueOf(v reflect.Value) pref.Value {
if v.Type() != c.goType {
panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), c.goType))
}
return pref.ValueOf(&listReflect{v, c.c})
}
func (c *listConverter) GoValueOf(v pref.Value) reflect.Value {
return v.List().(*listReflect).v
}
func (c *listConverter) New() pref.Value {
return c.PBValueOf(reflect.New(c.goType.Elem()))
}
type listReflect struct {
@ -45,7 +62,7 @@ func (ls *listReflect) Truncate(i int) {
ls.v.Elem().Set(ls.v.Elem().Slice(0, i))
}
func (ls *listReflect) NewMessage() pref.Message {
return ls.conv.NewMessage()
return ls.conv.New().Message()
}
func (ls *listReflect) ProtoUnwrap() interface{} {
return ls.v.Interface()

View File

@ -2,74 +2,80 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package value
package impl
import (
"fmt"
"reflect"
pref "google.golang.org/protobuf/reflect/protoreflect"
)
// MapOf returns a protoreflect.Map view of p, which must a *map[K]V.
// If p is nil, this returns an empty, read-only map.
func MapOf(p interface{}, kc, kv Converter) interface {
pref.Map
Unwrapper
} {
// TODO: Validate that p is a *map[K]V?
rv := reflect.ValueOf(p)
return &mapReflect{rv, kc, kv}
type mapConverter struct {
goType reflect.Type
keyConv, valConv Converter
}
func newMapConverter(t reflect.Type, fd pref.FieldDescriptor) Converter {
if t.Kind() != reflect.Map {
panic(fmt.Sprintf("invalid Go type %v for field %v", t, fd.FullName()))
}
return &mapConverter{
goType: t,
keyConv: newSingularConverter(t.Key(), fd.MapKey()),
valConv: newSingularConverter(t.Elem(), fd.MapValue()),
}
}
func (c *mapConverter) PBValueOf(v reflect.Value) pref.Value {
if v.Type() != c.goType {
panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), c.goType))
}
return pref.ValueOf(&mapReflect{v, c.keyConv, c.valConv})
}
func (c *mapConverter) GoValueOf(v pref.Value) reflect.Value {
return v.Map().(*mapReflect).v
}
func (c *mapConverter) New() pref.Value {
return c.PBValueOf(reflect.MakeMap(c.goType))
}
type mapReflect struct {
v reflect.Value // *map[K]V
v reflect.Value // map[K]V
keyConv Converter
valConv Converter
}
func (ms *mapReflect) Len() int {
if ms.v.IsNil() {
return 0
}
return ms.v.Elem().Len()
return ms.v.Len()
}
func (ms *mapReflect) Has(k pref.MapKey) bool {
if ms.v.IsNil() {
return false
}
rk := ms.keyConv.GoValueOf(k.Value())
rv := ms.v.Elem().MapIndex(rk)
rv := ms.v.MapIndex(rk)
return rv.IsValid()
}
func (ms *mapReflect) Get(k pref.MapKey) pref.Value {
if ms.v.IsNil() {
return pref.Value{}
}
rk := ms.keyConv.GoValueOf(k.Value())
rv := ms.v.Elem().MapIndex(rk)
rv := ms.v.MapIndex(rk)
if !rv.IsValid() {
return pref.Value{}
}
return ms.valConv.PBValueOf(rv)
}
func (ms *mapReflect) Set(k pref.MapKey, v pref.Value) {
if ms.v.Elem().IsNil() {
ms.v.Elem().Set(reflect.MakeMap(ms.v.Elem().Type()))
}
rk := ms.keyConv.GoValueOf(k.Value())
rv := ms.valConv.GoValueOf(v)
ms.v.Elem().SetMapIndex(rk, rv)
ms.v.SetMapIndex(rk, rv)
}
func (ms *mapReflect) Clear(k pref.MapKey) {
rk := ms.keyConv.GoValueOf(k.Value())
ms.v.Elem().SetMapIndex(rk, reflect.Value{})
ms.v.SetMapIndex(rk, reflect.Value{})
}
func (ms *mapReflect) Range(f func(pref.MapKey, pref.Value) bool) {
if ms.v.IsNil() {
return
}
for _, k := range ms.v.Elem().MapKeys() {
if v := ms.v.Elem().MapIndex(k); v.IsValid() {
for _, k := range ms.v.MapKeys() {
if v := ms.v.MapIndex(k); v.IsValid() {
pk := ms.keyConv.PBValueOf(k).MapKey()
pv := ms.valConv.PBValueOf(v)
if !f(pk, pv) {
@ -79,7 +85,7 @@ func (ms *mapReflect) Range(f func(pref.MapKey, pref.Value) bool) {
}
}
func (ms *mapReflect) NewMessage() pref.Message {
return ms.valConv.NewMessage()
return ms.valConv.New().Message()
}
func (ms *mapReflect) ProtoUnwrap() interface{} {
return ms.v.Interface()

View File

@ -11,7 +11,6 @@ import (
"sync"
"google.golang.org/protobuf/internal/filedesc"
pvalue "google.golang.org/protobuf/internal/value"
"google.golang.org/protobuf/reflect/protoreflect"
pref "google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/prototype"
@ -80,8 +79,8 @@ func (e *legacyEnumWrapper) ProtoUnwrap() interface{} {
}
var (
_ pref.Enum = (*legacyEnumWrapper)(nil)
_ pvalue.Unwrapper = (*legacyEnumWrapper)(nil)
_ pref.Enum = (*legacyEnumWrapper)(nil)
_ Unwrapper = (*legacyEnumWrapper)(nil)
)
var legacyEnumDescCache sync.Map // map[reflect.Type]protoreflect.EnumDescriptor

View File

@ -12,10 +12,8 @@ import (
"google.golang.org/protobuf/internal/descfmt"
ptag "google.golang.org/protobuf/internal/encoding/tag"
"google.golang.org/protobuf/internal/filedesc"
pvalue "google.golang.org/protobuf/internal/value"
pref "google.golang.org/protobuf/reflect/protoreflect"
preg "google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/reflect/prototype"
piface "google.golang.org/protobuf/runtime/protoiface"
)
@ -70,7 +68,7 @@ func legacyExtensionDescFromType(xt pref.ExtensionType) *piface.ExtensionDescV1
// Create a new parent message and unwrap it if possible.
mv := mt.New().Interface()
t := reflect.TypeOf(mv)
if mv, ok := mv.(pvalue.Unwrapper); ok {
if mv, ok := mv.(Unwrapper); ok {
t = reflect.TypeOf(mv.ProtoUnwrap())
}
@ -198,7 +196,13 @@ func legacyExtensionTypeFromDesc(d *piface.ExtensionDescV1) pref.ExtensionType {
xd.L1.Extendee = Export{}.MessageDescriptorOf(d.ExtendedType)
xd.L2.Enum = ed
xd.L2.Message = md
xt := LegacyExtensionTypeOf(xd, t)
tt := reflect.TypeOf(d.ExtensionType)
if isOptional {
tt = tt.Elem()
} else if isRepeated {
tt = reflect.PtrTo(tt)
}
xt := LegacyExtensionTypeOf(xd, tt)
// Cache the conversion for both directions.
legacyExtensionDescCache.LoadOrStore(xt, d)
@ -209,82 +213,30 @@ func legacyExtensionTypeFromDesc(d *piface.ExtensionDescV1) pref.ExtensionType {
}
// LegacyExtensionTypeOf returns a protoreflect.ExtensionType where the
// element type of the field is t. The type t must be provided if the field
// is an enum or message.
// element type of the field is t.
//
// This is exported for testing purposes.
func LegacyExtensionTypeOf(xd pref.ExtensionDescriptor, t reflect.Type) pref.ExtensionType {
var conv pvalue.Converter
var isLegacy bool
xt := &prototype.Extension{ExtensionDescriptor: xd}
switch xd.Kind() {
case pref.EnumKind:
conv, isLegacy = newConverter(t, xd.Kind())
xt.NewEnum = conv.NewEnum
case pref.MessageKind, pref.GroupKind:
conv, isLegacy = newConverter(t, xd.Kind())
xt.NewMessage = conv.NewMessage
default:
// Extension types for non-enums and non-messages are simple.
return &prototype.Extension{ExtensionDescriptor: xd}
return &legacyExtensionType{
ExtensionDescriptor: xd,
typ: t,
conv: NewConverter(t, xd),
}
if !isLegacy {
return xt
}
// Wrap ExtensionType such that GoType presents the legacy Go type.
xt2 := &legacyExtensionType{ExtensionType: xt}
if xd.Cardinality() != pref.Repeated {
xt2.typ = t
xt2.new = func() pref.Value {
return xt.New()
}
xt2.valueOf = func(v interface{}) pref.Value {
if reflect.TypeOf(v) != xt2.typ {
panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
}
if xd.Kind() == pref.EnumKind {
return xt.ValueOf(Export{}.EnumOf(v))
} else {
return xt.ValueOf(Export{}.MessageOf(v).Interface())
}
}
xt2.interfaceOf = func(v pref.Value) interface{} {
return xt.InterfaceOf(v).(pvalue.Unwrapper).ProtoUnwrap()
}
} else {
xt2.typ = reflect.PtrTo(reflect.SliceOf(t))
xt2.new = func() pref.Value {
v := reflect.New(xt2.typ.Elem()).Interface()
return pref.ValueOf(pvalue.ListOf(v, conv))
}
xt2.valueOf = func(v interface{}) pref.Value {
if reflect.TypeOf(v) != xt2.typ {
panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
}
return pref.ValueOf(pvalue.ListOf(v, conv))
}
xt2.interfaceOf = func(pv pref.Value) interface{} {
v := pv.List().(pvalue.Unwrapper).ProtoUnwrap()
if reflect.TypeOf(v) != xt2.typ {
panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
}
return v
}
}
return xt2
}
type legacyExtensionType struct {
pref.ExtensionType
typ reflect.Type
new func() pref.Value
valueOf func(interface{}) pref.Value
interfaceOf func(pref.Value) interface{}
pref.ExtensionDescriptor
typ reflect.Type
conv Converter
}
func (x *legacyExtensionType) GoType() reflect.Type { return x.typ }
func (x *legacyExtensionType) New() pref.Value { return x.new() }
func (x *legacyExtensionType) ValueOf(v interface{}) pref.Value { return x.valueOf(v) }
func (x *legacyExtensionType) InterfaceOf(v pref.Value) interface{} { return x.interfaceOf(v) }
func (x *legacyExtensionType) GoType() reflect.Type { return x.typ }
func (x *legacyExtensionType) New() pref.Value { return x.conv.New() }
func (x *legacyExtensionType) ValueOf(v interface{}) pref.Value {
return x.conv.PBValueOf(reflect.ValueOf(v))
}
func (x *legacyExtensionType) InterfaceOf(v pref.Value) interface{} {
return x.conv.GoValueOf(v).Interface()
}
func (x *legacyExtensionType) Descriptor() pref.ExtensionDescriptor { return x.ExtensionDescriptor }
func (x *legacyExtensionType) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, x) }

View File

@ -56,10 +56,10 @@ func init() {
preg.GlobalTypes.Register(mt)
}
func mustMakeExtensionType(fileDesc, extDesc string, t interface{}, r pdesc.Resolver) pref.ExtensionType {
func mustMakeExtensionType(fileDesc, extDesc string, t reflect.Type, r pdesc.Resolver) pref.ExtensionType {
s := fmt.Sprintf(`name:"test.proto" syntax:"proto2" %s extension:[{%s}]`, fileDesc, extDesc)
xd := mustMakeFileDesc(s, r).Extensions().Get(0)
return pimpl.LegacyExtensionTypeOf(xd, reflect.TypeOf(t))
return pimpl.LegacyExtensionTypeOf(xd, t)
}
func mustMakeFileDesc(s string, r pdesc.Resolver) pref.FileDescriptor {
@ -92,102 +92,102 @@ var (
mustMakeExtensionType(
`package:"fizz.buzz" dependency:"legacy.proto"`,
`name:"optional_bool" number:10000 label:LABEL_OPTIONAL type:TYPE_BOOL default_value:"true" extendee:".LegacyTestMessage"`,
nil, depReg,
reflect.TypeOf(false), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:"legacy.proto"`,
`name:"optional_int32" number:10001 label:LABEL_OPTIONAL type:TYPE_INT32 default_value:"-12345" extendee:".LegacyTestMessage"`,
nil, depReg,
reflect.TypeOf(int32(0)), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:"legacy.proto"`,
`name:"optional_uint32" number:10002 label:LABEL_OPTIONAL type:TYPE_UINT32 default_value:"3200" extendee:".LegacyTestMessage"`,
nil, depReg,
reflect.TypeOf(uint32(0)), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:"legacy.proto"`,
`name:"optional_float" number:10003 label:LABEL_OPTIONAL type:TYPE_FLOAT default_value:"3.14159" extendee:".LegacyTestMessage"`,
nil, depReg,
reflect.TypeOf(float32(0)), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:"legacy.proto"`,
`name:"optional_string" number:10004 label:LABEL_OPTIONAL type:TYPE_STRING default_value:"hello, \"world!\"\n" extendee:".LegacyTestMessage"`,
nil, depReg,
reflect.TypeOf(""), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:"legacy.proto"`,
`name:"optional_bytes" number:10005 label:LABEL_OPTIONAL type:TYPE_BYTES default_value:"dead\\336\\255\\276\\357beef" extendee:".LegacyTestMessage"`,
nil, depReg,
reflect.TypeOf(([]byte)(nil)), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:["legacy.proto", "proto2.v1.0.0-20180125-92554152/test.proto"]`,
`name:"optional_enum_v1" number:10006 label:LABEL_OPTIONAL type:TYPE_ENUM type_name:".google.golang.org.proto2_20180125.Message.ChildEnum" default_value:"ALPHA" extendee:".LegacyTestMessage"`,
proto2_20180125.Message_ChildEnum(0), depReg,
reflect.TypeOf(proto2_20180125.Message_ChildEnum(0)), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:["legacy.proto", "proto2.v1.0.0-20180125-92554152/test.proto"]`,
`name:"optional_message_v1" number:10007 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:".google.golang.org.proto2_20180125.Message.ChildMessage" extendee:".LegacyTestMessage"`,
(*proto2_20180125.Message_ChildMessage)(nil), depReg,
reflect.TypeOf((*proto2_20180125.Message_ChildMessage)(nil)), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:["legacy.proto", "enum2.proto"]`,
`name:"optional_enum_v2" number:10008 label:LABEL_OPTIONAL type:TYPE_ENUM type_name:".EnumProto2" default_value:"DEAD" extendee:".LegacyTestMessage"`,
EnumProto2(0), depReg,
reflect.TypeOf(EnumProto2(0)), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:["legacy.proto", "enum-messages.proto"]`,
`name:"optional_message_v2" number:10009 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:".EnumMessages" extendee:".LegacyTestMessage"`,
(*EnumMessages)(nil), depReg,
reflect.TypeOf((*EnumMessages)(nil)), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:"legacy.proto"`,
`name:"repeated_bool" number:10010 label:LABEL_REPEATED type:TYPE_BOOL extendee:".LegacyTestMessage"`,
nil, depReg,
reflect.TypeOf((*[]bool)(nil)), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:"legacy.proto"`,
`name:"repeated_int32" number:10011 label:LABEL_REPEATED type:TYPE_INT32 extendee:".LegacyTestMessage"`,
nil, depReg,
reflect.TypeOf((*[]int32)(nil)), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:"legacy.proto"`,
`name:"repeated_uint32" number:10012 label:LABEL_REPEATED type:TYPE_UINT32 extendee:".LegacyTestMessage"`,
nil, depReg,
reflect.TypeOf((*[]uint32)(nil)), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:"legacy.proto"`,
`name:"repeated_float" number:10013 label:LABEL_REPEATED type:TYPE_FLOAT extendee:".LegacyTestMessage"`,
nil, depReg,
reflect.TypeOf((*[]float32)(nil)), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:"legacy.proto"`,
`name:"repeated_string" number:10014 label:LABEL_REPEATED type:TYPE_STRING extendee:".LegacyTestMessage"`,
nil, depReg,
reflect.TypeOf((*[]string)(nil)), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:"legacy.proto"`,
`name:"repeated_bytes" number:10015 label:LABEL_REPEATED type:TYPE_BYTES extendee:".LegacyTestMessage"`,
nil, depReg,
reflect.TypeOf((*[][]byte)(nil)), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:["legacy.proto", "proto2.v1.0.0-20180125-92554152/test.proto"]`,
`name:"repeated_enum_v1" number:10016 label:LABEL_REPEATED type:TYPE_ENUM type_name:".google.golang.org.proto2_20180125.Message.ChildEnum" extendee:".LegacyTestMessage"`,
proto2_20180125.Message_ChildEnum(0), depReg,
reflect.TypeOf((*[]proto2_20180125.Message_ChildEnum)(nil)), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:["legacy.proto", "proto2.v1.0.0-20180125-92554152/test.proto"]`,
`name:"repeated_message_v1" number:10017 label:LABEL_REPEATED type:TYPE_MESSAGE type_name:".google.golang.org.proto2_20180125.Message.ChildMessage" extendee:".LegacyTestMessage"`,
(*proto2_20180125.Message_ChildMessage)(nil), depReg,
reflect.TypeOf((*[]*proto2_20180125.Message_ChildMessage)(nil)), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:["legacy.proto", "enum2.proto"]`,
`name:"repeated_enum_v2" number:10018 label:LABEL_REPEATED type:TYPE_ENUM type_name:".EnumProto2" extendee:".LegacyTestMessage"`,
EnumProto2(0), depReg,
reflect.TypeOf((*[]EnumProto2)(nil)), depReg,
),
mustMakeExtensionType(
`package:"fizz.buzz" dependency:["legacy.proto", "enum-messages.proto"]`,
`name:"repeated_message_v2" number:10019 label:LABEL_REPEATED type:TYPE_MESSAGE type_name:".EnumMessages" extendee:".LegacyTestMessage"`,
(*EnumMessages)(nil), depReg,
reflect.TypeOf((*[](*EnumMessages))(nil)), depReg,
),
}

View File

@ -11,7 +11,6 @@ import (
"sync"
"google.golang.org/protobuf/internal/flags"
pvalue "google.golang.org/protobuf/internal/value"
pref "google.golang.org/protobuf/reflect/protoreflect"
preg "google.golang.org/protobuf/reflect/protoregistry"
piface "google.golang.org/protobuf/runtime/protoiface"
@ -40,10 +39,11 @@ func fieldInfoForOneof(fd pref.FieldDescriptor, fs reflect.StructField, x export
if !reflect.PtrTo(ot).Implements(ft) {
panic(fmt.Sprintf("invalid type: %v does not implement %v", ot, ft))
}
conv, _ := newConverter(ot.Field(0).Type, fd.Kind())
conv := NewConverter(ot.Field(0).Type, fd)
isMessage := fd.Message() != nil
var frozenEmpty pref.Value
if conv.NewMessage != nil {
frozenEmpty = pref.ValueOf(frozenMessage{conv.NewMessage()})
if isMessage {
frozenEmpty = pref.ValueOf(frozenMessage{conv.New().Message()})
}
// TODO: Implement unsafe fast path?
@ -97,7 +97,7 @@ func fieldInfoForOneof(fd pref.FieldDescriptor, fs reflect.StructField, x export
rv.Set(conv.GoValueOf(v))
},
mutable: func(p pointer) pref.Value {
if conv.NewMessage == nil {
if !isMessage {
panic("invalid Mutable on field with non-composite type")
}
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
@ -106,11 +106,13 @@ func fieldInfoForOneof(fd pref.FieldDescriptor, fs reflect.StructField, x export
}
rv = rv.Elem().Elem().Field(0)
if rv.IsNil() {
rv.Set(conv.GoValueOf(pref.ValueOf(conv.NewMessage())))
rv.Set(conv.GoValueOf(pref.ValueOf(conv.New().Message())))
}
return conv.PBValueOf(rv)
},
newMessage: conv.NewMessage,
newMessage: func() pref.Message {
return conv.New().Message()
},
}
}
@ -119,57 +121,8 @@ func fieldInfoForMap(fd pref.FieldDescriptor, fs reflect.StructField, x exporter
if ft.Kind() != reflect.Map {
panic(fmt.Sprintf("invalid type: got %v, want map kind", ft))
}
keyConv, _ := newConverter(ft.Key(), fd.MapKey().Kind())
valConv, _ := newConverter(ft.Elem(), fd.MapValue().Kind())
frozenEmpty := pref.ValueOf(frozenMap{
pvalue.MapOf(reflect.Zero(reflect.PtrTo(fs.Type)).Interface(), keyConv, valConv),
})
// TODO: Implement unsafe fast path?
fieldOffset := offsetOf(fs, x)
return fieldInfo{
fieldDesc: fd,
has: func(p pointer) bool {
if p.IsNil() {
return false
}
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
return rv.Len() > 0
},
clear: func(p pointer) {
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
rv.Set(reflect.Zero(rv.Type()))
},
get: func(p pointer) pref.Value {
if p.IsNil() {
return frozenEmpty
}
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
if rv.IsNil() {
return frozenEmpty
}
return pref.ValueOf(pvalue.MapOf(rv.Addr().Interface(), keyConv, valConv))
},
set: func(p pointer, v pref.Value) {
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
rv.Set(reflect.ValueOf(v.Map().(pvalue.Unwrapper).ProtoUnwrap()).Elem())
},
mutable: func(p pointer) pref.Value {
v := p.Apply(fieldOffset).AsIfaceOf(fs.Type)
return pref.ValueOf(pvalue.MapOf(v, keyConv, valConv))
},
}
}
func fieldInfoForList(fd pref.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
ft := fs.Type
if ft.Kind() != reflect.Slice {
panic(fmt.Sprintf("invalid type: got %v, want slice kind", ft))
}
conv, _ := newConverter(ft.Elem(), fd.Kind())
frozenEmpty := pref.ValueOf(frozenList{
pvalue.ListOf(reflect.Zero(reflect.PtrTo(fs.Type)).Interface(), conv),
})
conv := NewConverter(ft, fd)
frozenEmpty := pref.ValueOf(frozenMap{conv.New().Map()})
// TODO: Implement unsafe fast path?
fieldOffset := offsetOf(fs, x)
@ -194,15 +147,62 @@ func fieldInfoForList(fd pref.FieldDescriptor, fs reflect.StructField, x exporte
if rv.Len() == 0 {
return frozenEmpty
}
return pref.ValueOf(pvalue.ListOf(rv.Addr().Interface(), conv))
return conv.PBValueOf(rv)
},
set: func(p pointer, v pref.Value) {
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
rv.Set(reflect.ValueOf(v.List().(pvalue.Unwrapper).ProtoUnwrap()).Elem())
rv.Set(conv.GoValueOf(v))
},
mutable: func(p pointer) pref.Value {
v := p.Apply(fieldOffset).AsIfaceOf(fs.Type)
return pref.ValueOf(pvalue.ListOf(v, conv))
v := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
if v.IsNil() {
v.Set(reflect.MakeMap(fs.Type))
}
return conv.PBValueOf(v)
},
}
}
func fieldInfoForList(fd pref.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
ft := fs.Type
if ft.Kind() != reflect.Slice {
panic(fmt.Sprintf("invalid type: got %v, want slice kind", ft))
}
conv := NewConverter(reflect.PtrTo(ft), fd)
frozenEmpty := pref.ValueOf(frozenList{conv.New().List()})
// TODO: Implement unsafe fast path?
fieldOffset := offsetOf(fs, x)
return fieldInfo{
fieldDesc: fd,
has: func(p pointer) bool {
if p.IsNil() {
return false
}
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
return rv.Len() > 0
},
clear: func(p pointer) {
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
rv.Set(reflect.Zero(rv.Type()))
},
get: func(p pointer) pref.Value {
if p.IsNil() {
return frozenEmpty
}
rv := p.Apply(fieldOffset).AsValueOf(fs.Type)
if rv.Elem().Len() == 0 {
return frozenEmpty
}
return conv.PBValueOf(rv)
},
set: func(p pointer, v pref.Value) {
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
rv.Set(reflect.ValueOf(v.List().(Unwrapper).ProtoUnwrap()).Elem())
},
mutable: func(p pointer) pref.Value {
v := p.Apply(fieldOffset).AsValueOf(fs.Type)
return conv.PBValueOf(v)
},
}
}
@ -224,7 +224,7 @@ func fieldInfoForScalar(fd pref.FieldDescriptor, fs reflect.StructField, x expor
ft = ft.Elem()
}
}
conv, _ := newConverter(ft, fd.Kind())
conv := NewConverter(ft, fd)
// TODO: Implement unsafe fast path?
fieldOffset := offsetOf(fs, x)
@ -372,8 +372,8 @@ func fieldInfoForWeakMessage(fd pref.FieldDescriptor, weakOffset offset) fieldIn
func fieldInfoForMessage(fd pref.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
ft := fs.Type
conv, _ := newConverter(ft, fd.Kind())
frozenEmpty := pref.ValueOf(frozenMessage{conv.NewMessage()})
conv := NewConverter(ft, fd)
frozenEmpty := pref.ValueOf(frozenMessage{conv.New().Message()})
// TODO: Implement unsafe fast path?
fieldOffset := offsetOf(fs, x)
@ -410,11 +410,13 @@ func fieldInfoForMessage(fd pref.FieldDescriptor, fs reflect.StructField, x expo
mutable: func(p pointer) pref.Value {
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
if rv.IsNil() {
rv.Set(conv.GoValueOf(pref.ValueOf(conv.NewMessage())))
rv.Set(conv.GoValueOf(conv.New()))
}
return conv.PBValueOf(rv)
},
newMessage: conv.NewMessage,
newMessage: func() pref.Message {
return conv.New().Message()
},
}
}
@ -446,50 +448,6 @@ var (
messageIfaceV2 = reflect.TypeOf((*pref.ProtoMessage)(nil)).Elem()
)
func newConverter(t reflect.Type, k pref.Kind) (conv pvalue.Converter, isLegacy bool) {
switch k {
case pref.EnumKind:
if t.Kind() == reflect.Int32 && !t.Implements(enumIfaceV2) {
return pvalue.Converter{
PBValueOf: func(v reflect.Value) pref.Value {
if v.Type() != t {
panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
}
return pref.ValueOf(pref.EnumNumber(v.Int()))
},
GoValueOf: func(v pref.Value) reflect.Value {
return reflect.ValueOf(v.Enum()).Convert(t)
},
NewEnum: func(n pref.EnumNumber) pref.Enum {
return legacyWrapEnum(reflect.ValueOf(n).Convert(t))
},
}, true
}
case pref.MessageKind, pref.GroupKind:
if t.Kind() == reflect.Ptr && t.Implements(messageIfaceV1) && !t.Implements(messageIfaceV2) {
return pvalue.Converter{
PBValueOf: func(v reflect.Value) pref.Value {
if v.Type() != t {
panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
}
return pref.ValueOf(Export{}.MessageOf(v.Interface()))
},
GoValueOf: func(v pref.Value) reflect.Value {
rv := reflect.ValueOf(v.Message().(pvalue.Unwrapper).ProtoUnwrap())
if rv.Type() != t {
panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), t))
}
return rv
},
NewMessage: func() pref.Message {
return legacyWrapMessage(reflect.New(t.Elem())).ProtoReflect()
},
}, true
}
}
return pvalue.NewConverter(t, k), false
}
// defaultValueOf returns the default value for the field.
func defaultValueOf(fd pref.FieldDescriptor) pref.Value {
if fd == nil {

View File

@ -9,7 +9,6 @@ import (
"reflect"
"google.golang.org/protobuf/internal/pragma"
pvalue "google.golang.org/protobuf/internal/value"
pref "google.golang.org/protobuf/reflect/protoreflect"
)
@ -66,8 +65,8 @@ type MessageState struct {
type messageState MessageState
var (
_ pref.Message = (*messageState)(nil)
_ pvalue.Unwrapper = (*messageState)(nil)
_ pref.Message = (*messageState)(nil)
_ Unwrapper = (*messageState)(nil)
)
// messageDataType is a tuple of a pointer to the message data and
@ -86,9 +85,9 @@ type (
var (
_ pref.Message = (*messageReflectWrapper)(nil)
_ pvalue.Unwrapper = (*messageReflectWrapper)(nil)
_ Unwrapper = (*messageReflectWrapper)(nil)
_ pref.ProtoMessage = (*messageIfaceWrapper)(nil)
_ pvalue.Unwrapper = (*messageIfaceWrapper)(nil)
_ Unwrapper = (*messageIfaceWrapper)(nil)
)
// MessageOf returns a reflective view over a message. The input must be a

View File

@ -5,63 +5,64 @@
package mapsort_test
import (
"reflect"
"strconv"
"testing"
"google.golang.org/protobuf/internal/mapsort"
"google.golang.org/protobuf/internal/value"
testpb "google.golang.org/protobuf/internal/testprotos/test"
pref "google.golang.org/protobuf/reflect/protoreflect"
)
func TestRange(t *testing.T) {
for _, test := range []struct {
mapv interface{}
kind pref.Kind
}{
{
mapv: &map[bool]int32{
false: 0,
true: 1,
},
kind: pref.BoolKind,
m := (&testpb.TestAllTypes{
MapBoolBool: map[bool]bool{
false: false,
true: true,
},
{
mapv: &map[int32]int32{
0: 0,
1: 1,
2: 2,
},
kind: pref.Int32Kind,
MapInt32Int32: map[int32]int32{
0: 0,
1: 1,
2: 2,
},
{
mapv: &map[uint64]int32{
0: 0,
1: 1,
2: 2,
},
kind: pref.Uint64Kind,
MapUint64Uint64: map[uint64]uint64{
0: 0,
1: 1,
2: 2,
},
{
mapv: &map[string]int32{
"a": 0,
"b": 1,
"c": 2,
},
kind: pref.StringKind,
MapStringString: map[string]string{
"0": "0",
"1": "1",
"2": "2",
},
} {
rv := reflect.TypeOf(test.mapv).Elem()
mapv := value.MapOf(test.mapv, value.NewConverter(rv.Key(), test.kind), value.NewConverter(rv.Elem(), pref.Int32Kind))
}).ProtoReflect()
m.Range(func(fd pref.FieldDescriptor, v pref.Value) bool {
mapv := v.Map()
var got []pref.MapKey
mapsort.Range(mapv, test.kind, func(key pref.MapKey, _ pref.Value) bool {
mapsort.Range(mapv, fd.MapKey().Kind(), func(key pref.MapKey, _ pref.Value) bool {
got = append(got, key)
return true
})
for i, key := range got {
if int64(i) != mapv.Get(key).Int() {
t.Errorf("out of order range over map: %v", got)
for wanti, key := range got {
var goti int
switch x := mapv.Get(key).Interface().(type) {
case bool:
if x {
goti = 1
}
case int32:
goti = int(x)
case uint64:
goti = int(x)
case string:
goti, _ = strconv.Atoi(x)
default:
t.Fatalf("unhandled map value type %T", x)
}
if wanti != goti {
t.Errorf("out of order range over map field %v: %v", fd.FullName(), got)
break
}
}
}
return true
})
}

View File

@ -1,160 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package value provides functionality for wrapping Go values to implement
// protoreflect values.
package value
import (
"fmt"
"reflect"
pref "google.golang.org/protobuf/reflect/protoreflect"
)
// Unwrapper unwraps the value to the underlying value.
// This is implemented by List and Map.
type Unwrapper interface {
ProtoUnwrap() interface{}
}
var (
boolType = reflect.TypeOf(bool(false))
int32Type = reflect.TypeOf(int32(0))
int64Type = reflect.TypeOf(int64(0))
uint32Type = reflect.TypeOf(uint32(0))
uint64Type = reflect.TypeOf(uint64(0))
float32Type = reflect.TypeOf(float32(0))
float64Type = reflect.TypeOf(float64(0))
stringType = reflect.TypeOf(string(""))
bytesType = reflect.TypeOf([]byte(nil))
enumIfaceV2 = reflect.TypeOf((*pref.Enum)(nil)).Elem()
messageIfaceV2 = reflect.TypeOf((*pref.ProtoMessage)(nil)).Elem()
byteType = reflect.TypeOf(byte(0))
)
// NewConverter matches a Go type with a protobuf kind and returns a Converter
// that converts between the two. Enums must be a named int32 kind that
// implements protoreflect.Enum, and messages must be pointer to a named
// struct type that implements protoreflect.ProtoMessage.
//
// This matcher deliberately supports a wider range of Go types than what
// protoc-gen-go historically generated to be able to automatically wrap some
// v1 messages generated by other forks of protoc-gen-go.
func NewConverter(t reflect.Type, k pref.Kind) Converter {
switch k {
case pref.BoolKind:
if t.Kind() == reflect.Bool {
return newScalarConverter(t, boolType)
}
case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
if t.Kind() == reflect.Int32 {
return newScalarConverter(t, int32Type)
}
case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
if t.Kind() == reflect.Int64 {
return newScalarConverter(t, int64Type)
}
case pref.Uint32Kind, pref.Fixed32Kind:
if t.Kind() == reflect.Uint32 {
return newScalarConverter(t, uint32Type)
}
case pref.Uint64Kind, pref.Fixed64Kind:
if t.Kind() == reflect.Uint64 {
return newScalarConverter(t, uint64Type)
}
case pref.FloatKind:
if t.Kind() == reflect.Float32 {
return newScalarConverter(t, float32Type)
}
case pref.DoubleKind:
if t.Kind() == reflect.Float64 {
return newScalarConverter(t, float64Type)
}
case pref.StringKind:
if t.Kind() == reflect.String || (t.Kind() == reflect.Slice && t.Elem() == byteType) {
return newScalarConverter(t, stringType)
}
case pref.BytesKind:
if t.Kind() == reflect.String || (t.Kind() == reflect.Slice && t.Elem() == byteType) {
return newScalarConverter(t, bytesType)
}
case pref.EnumKind:
// Handle enums, which must be a named int32 type.
if t.Implements(enumIfaceV2) && t.Kind() == reflect.Int32 {
return Converter{
PBValueOf: func(v reflect.Value) pref.Value {
if v.Type() != t {
panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
}
return pref.ValueOf(pref.EnumNumber(v.Int()))
},
GoValueOf: func(v pref.Value) reflect.Value {
return reflect.ValueOf(v.Enum()).Convert(t)
},
NewEnum: func(n pref.EnumNumber) pref.Enum {
return reflect.ValueOf(n).Convert(t).Interface().(pref.Enum)
},
}
}
case pref.MessageKind, pref.GroupKind:
// Handle v2 messages, which must satisfy the proto.Message interface.
if t.Implements(messageIfaceV2) && t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
return Converter{
PBValueOf: func(v reflect.Value) pref.Value {
if v.Type() != t {
panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
}
return pref.ValueOf(v.Interface().(pref.ProtoMessage).ProtoReflect())
},
GoValueOf: func(v pref.Value) reflect.Value {
rv := reflect.ValueOf(v.Message().Interface())
if rv.Type() != t {
panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), t))
}
return rv
},
NewMessage: func() pref.Message {
return reflect.New(t.Elem()).Interface().(pref.ProtoMessage).ProtoReflect()
},
}
}
}
panic(fmt.Sprintf("invalid Go type %v for protobuf kind %v", t, k))
}
func newScalarConverter(goType, pbType reflect.Type) Converter {
return Converter{
PBValueOf: func(v reflect.Value) pref.Value {
if v.Type() != goType {
panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), goType))
}
if goType.Kind() == reflect.String && pbType.Kind() == reflect.Slice && v.Len() == 0 {
return pref.ValueOf([]byte(nil)) // ensure empty string is []byte(nil)
}
return pref.ValueOf(v.Convert(pbType).Interface())
},
GoValueOf: func(v pref.Value) reflect.Value {
rv := reflect.ValueOf(v.Interface())
if rv.Type() != pbType {
panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), pbType))
}
if pbType.Kind() == reflect.String && goType.Kind() == reflect.Slice && rv.Len() == 0 {
return reflect.Zero(goType) // ensure empty string is []byte(nil)
}
return rv.Convert(goType)
},
}
}
// Converter provides functions for converting to/from Go reflect.Value types
// and protobuf protoreflect.Value types.
type Converter struct {
PBValueOf func(reflect.Value) pref.Value
GoValueOf func(pref.Value) reflect.Value
NewEnum func(pref.EnumNumber) pref.Enum
NewMessage func() pref.Message
}

View File

@ -12,7 +12,6 @@ import (
"sync"
"google.golang.org/protobuf/internal/descfmt"
"google.golang.org/protobuf/internal/value"
"google.golang.org/protobuf/reflect/protoreflect"
)
@ -109,250 +108,7 @@ func (t *Message) Format(s fmt.State, r rune) {
descfmt.FormatDesc(s, r, t)
}
// Extension is a protoreflect.ExtensionType which combines a
// protoreflect.ExtensionDescriptor with a constructor function.
//
// ExtensionDescriptor must be populated, while NewEnum or NewMessage must
// populated depending on the kind of the extension field.
// Once constructed, the exported fields must not be modified.
type Extension struct {
protoreflect.ExtensionDescriptor
// NewEnum constructs a new enum (see Enum.NewEnum).
// This must be populated if and only if ExtensionDescriptor.Kind
// is a protoreflect.EnumKind.
NewEnum func(protoreflect.EnumNumber) protoreflect.Enum
// NewMessage constructs a new message (see Enum.NewMessage).
// This must be populated if and only if ExtensionDescriptor.Kind
// is a protoreflect.MessageKind or protoreflect.GroupKind.
NewMessage func() protoreflect.Message
// TODO: Allow users to manually set new, valueOf, and interfaceOf.
// This allows users to implement custom composite types (e.g., List) or
// custom Go types for primitives (e.g., int32).
once sync.Once
goType reflect.Type
new func() protoreflect.Value
valueOf func(v interface{}) protoreflect.Value
interfaceOf func(v protoreflect.Value) interface{}
}
func (t *Extension) New() protoreflect.Value {
t.lazyInit()
pv := t.new()
v := t.interfaceOf(pv)
if reflect.TypeOf(v) != t.goType {
panic(fmt.Sprintf("invalid type: got %T, want %v", v, t.goType))
}
return pv
}
func (t *Extension) ValueOf(v interface{}) protoreflect.Value {
t.lazyInit()
if reflect.TypeOf(v) != t.goType {
panic(fmt.Sprintf("invalid type: got %T, want %v", v, t.goType))
}
return t.valueOf(v)
}
func (t *Extension) InterfaceOf(v protoreflect.Value) interface{} {
t.lazyInit()
vi := t.interfaceOf(v)
if reflect.TypeOf(vi) != t.goType {
panic(fmt.Sprintf("invalid type: got %T, want %v", vi, t.goType))
}
return vi
}
// GoType is the type of the extension field.
// The type is T for scalars and *[]T for lists (maps are not allowed).
// The type T is determined as follows:
//
// +------------+-------------------------------------+
// | Go type | Protobuf kind |
// +------------+-------------------------------------+
// | bool | BoolKind |
// | int32 | Int32Kind, Sint32Kind, Sfixed32Kind |
// | int64 | Int64Kind, Sint64Kind, Sfixed64Kind |
// | uint32 | Uint32Kind, Fixed32Kind |
// | uint64 | Uint64Kind, Fixed64Kind |
// | float32 | FloatKind |
// | float64 | DoubleKind |
// | string | StringKind |
// | []byte | BytesKind |
// | E | EnumKind |
// | M | MessageKind, GroupKind |
// +------------+-------------------------------------+
//
// The type E is the concrete enum type returned by NewEnum,
// which is often, but not required to be, a named int32 type.
// The type M is the concrete message type returned by NewMessage,
// which is often, but not required to be, a pointer to a named struct type.
func (t *Extension) GoType() reflect.Type {
t.lazyInit()
return t.goType
}
func (t *Extension) Descriptor() protoreflect.ExtensionDescriptor {
return t.ExtensionDescriptor
}
func (t *Extension) Format(s fmt.State, r rune) {
descfmt.FormatDesc(s, r, t)
}
func (t *Extension) lazyInit() {
t.once.Do(func() {
switch t.Kind() {
case protoreflect.EnumKind:
if t.NewEnum == nil || t.NewMessage != nil {
panic("NewEnum alone must be set")
}
e := t.NewEnum(0)
if e.Descriptor() != t.Enum() {
panic(fmt.Sprintf("mismatching enum descriptor: got %v, want %v", e.Descriptor(), t.Enum()))
}
t.goType = reflect.TypeOf(e)
case protoreflect.MessageKind, protoreflect.GroupKind:
if t.NewEnum != nil || t.NewMessage == nil {
panic("NewMessage alone must be set")
}
m := t.NewMessage()
if m.Descriptor() != t.Message() {
panic(fmt.Sprintf("mismatching message descriptor: got %v, want %v", m.Descriptor(), t.Message()))
}
t.goType = reflect.TypeOf(m.Interface())
default:
if t.NewEnum != nil || t.NewMessage != nil {
panic("neither NewEnum nor NewMessage may be set")
}
t.goType = goTypeForPBKind[t.Kind()]
}
switch t.Cardinality() {
case protoreflect.Optional:
switch t.Kind() {
case protoreflect.EnumKind:
t.new = func() protoreflect.Value {
return t.Default()
}
t.valueOf = func(v interface{}) protoreflect.Value {
ev := v.(protoreflect.Enum)
return protoreflect.ValueOf(ev.Number())
}
t.interfaceOf = func(v protoreflect.Value) interface{} {
return t.NewEnum(v.Enum())
}
case protoreflect.MessageKind, protoreflect.GroupKind:
t.new = func() protoreflect.Value {
return protoreflect.ValueOf(t.NewMessage())
}
t.valueOf = func(v interface{}) protoreflect.Value {
mv := v.(protoreflect.ProtoMessage).ProtoReflect()
return protoreflect.ValueOf(mv)
}
t.interfaceOf = func(v protoreflect.Value) interface{} {
return v.Message().Interface()
}
default:
t.new = func() protoreflect.Value {
v := t.Default()
if t.Kind() == protoreflect.BytesKind {
// Copy default bytes to avoid aliasing the original.
v = protoreflect.ValueOf(append([]byte(nil), v.Bytes()...))
}
return v
}
t.valueOf = func(v interface{}) protoreflect.Value {
return protoreflect.ValueOf(v)
}
t.interfaceOf = func(v protoreflect.Value) interface{} {
return v.Interface()
}
}
case protoreflect.Repeated:
var conv value.Converter
elemType := t.goType
switch t.Kind() {
case protoreflect.EnumKind:
conv = value.Converter{
PBValueOf: func(v reflect.Value) protoreflect.Value {
if v.Type() != elemType {
panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), elemType))
}
e := v.Interface().(protoreflect.Enum)
return protoreflect.ValueOf(e.Number())
},
GoValueOf: func(v protoreflect.Value) reflect.Value {
rv := reflect.ValueOf(t.NewEnum(v.Enum()))
if rv.Type() != elemType {
panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), elemType))
}
return rv
},
NewEnum: t.NewEnum,
}
case protoreflect.MessageKind, protoreflect.GroupKind:
conv = value.Converter{
PBValueOf: func(v reflect.Value) protoreflect.Value {
if v.Type() != elemType {
panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), elemType))
}
m := v.Interface().(protoreflect.ProtoMessage).ProtoReflect()
return protoreflect.ValueOf(m)
},
GoValueOf: func(v protoreflect.Value) reflect.Value {
rv := reflect.ValueOf(v.Message().Interface())
if rv.Type() != elemType {
panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), elemType))
}
return rv
},
NewMessage: t.NewMessage,
}
default:
conv = value.NewConverter(elemType, t.Kind())
}
t.goType = reflect.PtrTo(reflect.SliceOf(elemType))
t.new = func() protoreflect.Value {
v := reflect.New(t.goType.Elem()).Interface()
return protoreflect.ValueOf(value.ListOf(v, conv))
}
t.valueOf = func(v interface{}) protoreflect.Value {
return protoreflect.ValueOf(value.ListOf(v, conv))
}
t.interfaceOf = func(v protoreflect.Value) interface{} {
return v.List().(value.Unwrapper).ProtoUnwrap()
}
default:
panic(fmt.Sprintf("invalid cardinality: %v", t.Cardinality()))
}
})
}
var goTypeForPBKind = map[protoreflect.Kind]reflect.Type{
protoreflect.BoolKind: reflect.TypeOf(bool(false)),
protoreflect.Int32Kind: reflect.TypeOf(int32(0)),
protoreflect.Sint32Kind: reflect.TypeOf(int32(0)),
protoreflect.Sfixed32Kind: reflect.TypeOf(int32(0)),
protoreflect.Int64Kind: reflect.TypeOf(int64(0)),
protoreflect.Sint64Kind: reflect.TypeOf(int64(0)),
protoreflect.Sfixed64Kind: reflect.TypeOf(int64(0)),
protoreflect.Uint32Kind: reflect.TypeOf(uint32(0)),
protoreflect.Fixed32Kind: reflect.TypeOf(uint32(0)),
protoreflect.Uint64Kind: reflect.TypeOf(uint64(0)),
protoreflect.Fixed64Kind: reflect.TypeOf(uint64(0)),
protoreflect.FloatKind: reflect.TypeOf(float32(0)),
protoreflect.DoubleKind: reflect.TypeOf(float64(0)),
protoreflect.StringKind: reflect.TypeOf(string("")),
protoreflect.BytesKind: reflect.TypeOf([]byte(nil)),
}
var (
_ protoreflect.EnumType = (*Enum)(nil)
_ protoreflect.MessageType = (*Message)(nil)
_ protoreflect.ExtensionType = (*Extension)(nil)
_ protoreflect.EnumType = (*Enum)(nil)
_ protoreflect.MessageType = (*Message)(nil)
)