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>
diff --git a/internal/impl/convert.go b/internal/impl/convert.go
new file mode 100644
index 0000000..9016fa4
--- /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/impl/convert_list.go b/internal/impl/convert_list.go
new file mode 100644
index 0000000..a3ef76b
--- /dev/null
+++ b/internal/impl/convert_list.go
@@ -0,0 +1,69 @@
+// 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"
+)
+
+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 {
+ v reflect.Value // *[]T
+ conv Converter
+}
+
+func (ls *listReflect) Len() int {
+ if ls.v.IsNil() {
+ return 0
+ }
+ return ls.v.Elem().Len()
+}
+func (ls *listReflect) Get(i int) pref.Value {
+ return ls.conv.PBValueOf(ls.v.Elem().Index(i))
+}
+func (ls *listReflect) Set(i int, v pref.Value) {
+ ls.v.Elem().Index(i).Set(ls.conv.GoValueOf(v))
+}
+func (ls *listReflect) Append(v pref.Value) {
+ ls.v.Elem().Set(reflect.Append(ls.v.Elem(), ls.conv.GoValueOf(v)))
+}
+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.New().Message()
+}
+func (ls *listReflect) ProtoUnwrap() interface{} {
+ return ls.v.Interface()
+}
diff --git a/internal/impl/convert_map.go b/internal/impl/convert_map.go
new file mode 100644
index 0000000..69a93d7
--- /dev/null
+++ b/internal/impl/convert_map.go
@@ -0,0 +1,92 @@
+// 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"
+)
+
+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
+ keyConv Converter
+ valConv Converter
+}
+
+func (ms *mapReflect) Len() int {
+ return ms.v.Len()
+}
+func (ms *mapReflect) Has(k pref.MapKey) bool {
+ rk := ms.keyConv.GoValueOf(k.Value())
+ rv := ms.v.MapIndex(rk)
+ return rv.IsValid()
+}
+func (ms *mapReflect) Get(k pref.MapKey) pref.Value {
+ rk := ms.keyConv.GoValueOf(k.Value())
+ 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) {
+ rk := ms.keyConv.GoValueOf(k.Value())
+ rv := ms.valConv.GoValueOf(v)
+ ms.v.SetMapIndex(rk, rv)
+}
+func (ms *mapReflect) Clear(k pref.MapKey) {
+ rk := ms.keyConv.GoValueOf(k.Value())
+ ms.v.SetMapIndex(rk, reflect.Value{})
+}
+func (ms *mapReflect) Range(f func(pref.MapKey, pref.Value) bool) {
+ 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) {
+ return
+ }
+ }
+ }
+}
+func (ms *mapReflect) NewMessage() pref.Message {
+ 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 3c24eb3..142a90a 100644
--- a/internal/impl/legacy_enum.go
+++ b/internal/impl/legacy_enum.go
@@ -11,7 +11,6 @@
"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 @@
}
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 c8b13d8..e6e86fb 100644
--- a/internal/impl/legacy_extension.go
+++ b/internal/impl/legacy_extension.go
@@ -12,10 +12,8 @@
"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 @@
// 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 @@
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 @@
}
// 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 10c29b9..67f6011 100644
--- a/internal/impl/legacy_test.go
+++ b/internal/impl/legacy_test.go
@@ -56,10 +56,10 @@
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 @@
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 d6cc1b2..0c79c4c 100644
--- a/internal/impl/message_field.go
+++ b/internal/impl/message_field.go
@@ -11,7 +11,6 @@
"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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 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 @@
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 @@
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 fd5c8a9..46ada8f 100644
--- a/internal/impl/message_reflect.go
+++ b/internal/impl/message_reflect.go
@@ -9,7 +9,6 @@
"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 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 @@
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