From 954bd92c193e9c6979b5e1265cd7a267748e7b78 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Wed, 17 Jul 2019 16:52:10 -0700 Subject: [PATCH] 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 --- internal/filetype/build.go | 80 +++++- internal/impl/convert.go | 197 ++++++++++++++ .../{value/list.go => impl/convert_list.go} | 39 ++- .../{value/map.go => impl/convert_map.go} | 74 +++--- internal/impl/legacy_enum.go | 5 +- internal/impl/legacy_extension.go | 98 ++----- internal/impl/legacy_test.go | 44 ++-- internal/impl/message_field.go | 180 +++++-------- internal/impl/message_reflect.go | 9 +- internal/mapsort/mapsort_test.go | 83 +++--- internal/value/convert.go | 160 ----------- reflect/prototype/type.go | 248 +----------------- 12 files changed, 497 insertions(+), 720 deletions(-) create mode 100644 internal/impl/convert.go rename internal/{value/list.go => impl/convert_list.go} (53%) rename internal/{value/map.go => impl/convert_map.go} (50%) delete mode 100644 internal/value/convert.go diff --git a/internal/filetype/build.go b/internal/filetype/build.go index fa1bb9ac..da46141b 100644 --- a/internal/filetype/build.go +++ b/internal/filetype/build.go @@ -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 +} diff --git a/internal/impl/convert.go b/internal/impl/convert.go new file mode 100644 index 00000000..9016fa44 --- /dev/null +++ b/internal/impl/convert.go @@ -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())) +} diff --git a/internal/value/list.go b/internal/impl/convert_list.go similarity index 53% rename from internal/value/list.go rename to internal/impl/convert_list.go index ff46fa8b..a3ef76b1 100644 --- a/internal/value/list.go +++ b/internal/impl/convert_list.go @@ -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() diff --git a/internal/value/map.go b/internal/impl/convert_map.go similarity index 50% rename from internal/value/map.go rename to internal/impl/convert_map.go index c0fca110..69a93d74 100644 --- a/internal/value/map.go +++ b/internal/impl/convert_map.go @@ -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() diff --git a/internal/impl/legacy_enum.go b/internal/impl/legacy_enum.go index 3c24eb3b..142a90a9 100644 --- a/internal/impl/legacy_enum.go +++ b/internal/impl/legacy_enum.go @@ -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 diff --git a/internal/impl/legacy_extension.go b/internal/impl/legacy_extension.go index c8b13d8f..e6e86fb5 100644 --- a/internal/impl/legacy_extension.go +++ b/internal/impl/legacy_extension.go @@ -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) } diff --git a/internal/impl/legacy_test.go b/internal/impl/legacy_test.go index 10c29b95..67f6011c 100644 --- a/internal/impl/legacy_test.go +++ b/internal/impl/legacy_test.go @@ -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, ), } diff --git a/internal/impl/message_field.go b/internal/impl/message_field.go index d6cc1b24..0c79c4c8 100644 --- a/internal/impl/message_field.go +++ b/internal/impl/message_field.go @@ -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 { diff --git a/internal/impl/message_reflect.go b/internal/impl/message_reflect.go index fd5c8a97..46ada8fe 100644 --- a/internal/impl/message_reflect.go +++ b/internal/impl/message_reflect.go @@ -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 diff --git a/internal/mapsort/mapsort_test.go b/internal/mapsort/mapsort_test.go index bc33c4da..12de57c9 100644 --- a/internal/mapsort/mapsort_test.go +++ b/internal/mapsort/mapsort_test.go @@ -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 + }) } diff --git a/internal/value/convert.go b/internal/value/convert.go deleted file mode 100644 index eb8d5708..00000000 --- a/internal/value/convert.go +++ /dev/null @@ -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 -} diff --git a/reflect/prototype/type.go b/reflect/prototype/type.go index 0766b4b5..7bf61b89 100644 --- a/reflect/prototype/type.go +++ b/reflect/prototype/type.go @@ -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) )