reflect/prototype: initial commit
Add the prototype package which provides constructors for
protoreflect.{Enum,Message,Extension}Type.
Switch all usages of the internal/prototype equivalent to the new package.
Switch all custom implementions of {Enum,Message}Type to the new package.
Change-Id: Ia9dae6fed4f2b90e55c123627044a7faf098c4b1
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/178438
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/impl/export.go b/internal/impl/export.go
index 9ea3900..8c7c121 100644
--- a/internal/impl/export.go
+++ b/internal/impl/export.go
@@ -10,6 +10,7 @@
"google.golang.org/protobuf/encoding/prototext"
pref "google.golang.org/protobuf/reflect/protoreflect"
+ "google.golang.org/protobuf/reflect/prototype"
)
// Export is a zero-length named type that exists only to export a set of
@@ -31,24 +32,16 @@
// EnumTypeOf returns the protoreflect.EnumType for e.
func (Export) EnumTypeOf(e enum) pref.EnumType {
if ev, ok := e.(pref.Enum); ok {
- return &enumType{ev.Descriptor(), reflect.TypeOf(e)}
+ return &prototype.Enum{
+ EnumDescriptor: ev.Descriptor(),
+ NewEnum: func(n pref.EnumNumber) pref.Enum {
+ return reflect.ValueOf(n).Convert(reflect.TypeOf(e)).Interface().(pref.Enum)
+ },
+ }
}
return legacyWrapper.EnumTypeOf(e)
}
-// TODO: This needs to be centralized in a package.
-type enumType struct {
- // TODO: Remove me as an embedded field.
- pref.EnumDescriptor
- typ reflect.Type // must implement protoreflect.Enum
-}
-
-func (t *enumType) Descriptor() pref.EnumDescriptor { return t.EnumDescriptor }
-func (t *enumType) GoType() reflect.Type { return t.typ }
-func (t *enumType) New(n pref.EnumNumber) pref.Enum {
- return reflect.ValueOf(n).Convert(t.typ).Interface().(pref.Enum)
-}
-
// EnumDescriptorOf returns the protoreflect.EnumDescriptor for e.
func (Export) EnumDescriptorOf(e enum) pref.EnumDescriptor {
if ev, ok := e.(pref.Enum); ok {
@@ -82,24 +75,16 @@
// MessageTypeOf returns the protoreflect.MessageType for m.
func (Export) MessageTypeOf(m message) pref.MessageType {
if mv, ok := m.(pref.ProtoMessage); ok {
- return &messageType{mv.ProtoReflect().Descriptor(), reflect.TypeOf(m)}
+ return &prototype.Message{
+ MessageDescriptor: mv.ProtoReflect().Descriptor(),
+ NewMessage: func() pref.Message {
+ return reflect.New(reflect.TypeOf(m).Elem()).Interface().(pref.ProtoMessage).ProtoReflect()
+ },
+ }
}
return legacyWrapper.MessageTypeOf(m)
}
-// TODO: This needs to be centralized in a package.
-type messageType struct {
- // TODO: Remove me as an embedded field.
- pref.MessageDescriptor
- typ reflect.Type // must implement protoreflect.ProtoMessage
-}
-
-func (t *messageType) Descriptor() pref.MessageDescriptor { return t.MessageDescriptor }
-func (t *messageType) GoType() reflect.Type { return t.typ }
-func (t *messageType) New() pref.Message {
- return reflect.New(t.typ.Elem()).Interface().(pref.ProtoMessage).ProtoReflect()
-}
-
// MessageDescriptorOf returns the protoreflect.MessageDescriptor for m.
func (Export) MessageDescriptorOf(m message) pref.MessageDescriptor {
if mv, ok := m.(pref.ProtoMessage); ok {
diff --git a/internal/impl/legacy_hook.go b/internal/impl/legacy_hook.go
index f3ffe63..98eaf2f 100644
--- a/internal/impl/legacy_hook.go
+++ b/internal/impl/legacy_hook.go
@@ -4,14 +4,39 @@
package impl
-import pvalue "google.golang.org/protobuf/internal/value"
+import (
+ "reflect"
+
+ pvalue "google.golang.org/protobuf/internal/value"
+ pref "google.golang.org/protobuf/reflect/protoreflect"
+ piface "google.golang.org/protobuf/runtime/protoiface"
+)
// TODO: Add a default LegacyWrapper that panics with a more helpful message?
-var legacyWrapper pvalue.LegacyWrapper
+var legacyWrapper LegacyWrapper
// RegisterLegacyWrapper registers a set of constructor functions that are
// called when a legacy enum or message is encountered that does not natively
// support the protobuf reflection APIs.
-func RegisterLegacyWrapper(w pvalue.LegacyWrapper) {
+func RegisterLegacyWrapper(w LegacyWrapper) {
legacyWrapper = w
}
+
+// LegacyWrapper is a set of wrapper methods that wraps legacy v1 Go types
+// to implement the v2 reflection APIs.
+type LegacyWrapper interface {
+ NewConverter(reflect.Type, pref.Kind) pvalue.Converter
+
+ EnumOf(interface{}) pref.Enum
+ EnumTypeOf(interface{}) pref.EnumType
+ EnumDescriptorOf(interface{}) pref.EnumDescriptor
+
+ MessageOf(interface{}) pref.Message
+ MessageTypeOf(interface{}) pref.MessageType
+ MessageDescriptorOf(interface{}) pref.MessageDescriptor
+
+ // TODO: Remove these eventually.
+ // See the TODOs in internal/impl/legacy_extension.go.
+ ExtensionDescFromType(pref.ExtensionType) *piface.ExtensionDescV1
+ ExtensionTypeFromDesc(*piface.ExtensionDescV1) pref.ExtensionType
+}
diff --git a/internal/impl/message_field.go b/internal/impl/message_field.go
index 72f4523..a69d2c8 100644
--- a/internal/impl/message_field.go
+++ b/internal/impl/message_field.go
@@ -42,7 +42,7 @@
if !reflect.PtrTo(ot).Implements(ft) {
panic(fmt.Sprintf("invalid type: %v does not implement %v", ot, ft))
}
- conv := pvalue.NewLegacyConverter(ot.Field(0).Type, fd.Kind(), legacyWrapper)
+ conv := newConverter(ot.Field(0).Type, fd.Kind())
fieldOffset := offsetOf(fs)
// TODO: Implement unsafe fast path?
return fieldInfo{
@@ -86,12 +86,9 @@
}
rv.Set(reflect.Zero(rv.Type()))
},
- newMessage: func() pref.Message {
- // This is only valid for messages and panics for other kinds.
- return conv.MessageType.New()
- },
- offset: fieldOffset,
- isPointer: true,
+ newMessage: conv.NewMessage,
+ offset: fieldOffset,
+ isPointer: true,
}
}
@@ -100,8 +97,8 @@
if ft.Kind() != reflect.Map {
panic(fmt.Sprintf("invalid type: got %v, want map kind", ft))
}
- keyConv := pvalue.NewLegacyConverter(ft.Key(), fd.MapKey().Kind(), legacyWrapper)
- valConv := pvalue.NewLegacyConverter(ft.Elem(), fd.MapValue().Kind(), legacyWrapper)
+ keyConv := newConverter(ft.Key(), fd.MapKey().Kind())
+ valConv := newConverter(ft.Elem(), fd.MapValue().Kind())
wiretag := wire.EncodeTag(fd.Number(), wireTypes[fd.Kind()])
fieldOffset := offsetOf(fs)
// TODO: Implement unsafe fast path?
@@ -142,7 +139,7 @@
if ft.Kind() != reflect.Slice {
panic(fmt.Sprintf("invalid type: got %v, want slice kind", ft))
}
- conv := pvalue.NewLegacyConverter(ft.Elem(), fd.Kind(), legacyWrapper)
+ conv := newConverter(ft.Elem(), fd.Kind())
var wiretag uint64
if !fd.IsPacked() {
wiretag = wire.EncodeTag(fd.Number(), wireTypes[fd.Kind()])
@@ -197,7 +194,7 @@
ft = ft.Elem()
}
}
- conv := pvalue.NewLegacyConverter(ft, fd.Kind(), legacyWrapper)
+ conv := newConverter(ft, fd.Kind())
fieldOffset := offsetOf(fs)
wiretag := wire.EncodeTag(fd.Number(), wireTypes[fd.Kind()])
// TODO: Implement unsafe fast path?
@@ -267,7 +264,7 @@
func fieldInfoForMessage(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo {
ft := fs.Type
- conv := pvalue.NewLegacyConverter(ft, fd.Kind(), legacyWrapper)
+ conv := newConverter(ft, fd.Kind())
fieldOffset := offsetOf(fs)
// TODO: Implement unsafe fast path?
wiretag := wire.EncodeTag(fd.Number(), wireTypes[fd.Kind()])
@@ -300,14 +297,12 @@
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
rv.Set(reflect.Zero(rv.Type()))
},
- newMessage: func() pref.Message {
- return conv.MessageType.New()
- },
- funcs: fieldCoder(fd, ft),
- offset: fieldOffset,
- isPointer: true,
- wiretag: wiretag,
- tagsize: wire.SizeVarint(wiretag),
+ newMessage: conv.NewMessage,
+ funcs: fieldCoder(fd, ft),
+ offset: fieldOffset,
+ isPointer: true,
+ wiretag: wiretag,
+ tagsize: wire.SizeVarint(wiretag),
}
}
@@ -342,3 +337,10 @@
},
}
}
+
+func newConverter(t reflect.Type, k pref.Kind) pvalue.Converter {
+ if legacyWrapper != nil {
+ return legacyWrapper.NewConverter(t, k)
+ }
+ return pvalue.NewConverter(t, k)
+}
diff --git a/internal/impl/message_test.go b/internal/impl/message_test.go
index 26f8db6..e1fc1ce 100644
--- a/internal/impl/message_test.go
+++ b/internal/impl/message_test.go
@@ -19,6 +19,7 @@
scalar "google.golang.org/protobuf/internal/scalar"
pvalue "google.golang.org/protobuf/internal/value"
pref "google.golang.org/protobuf/reflect/protoreflect"
+ "google.golang.org/protobuf/reflect/prototype"
proto2_20180125 "google.golang.org/protobuf/internal/testprotos/legacy/proto2.v1.0.0-20180125-92554152"
"google.golang.org/protobuf/types/descriptorpb"
@@ -189,8 +190,8 @@
MapBytes map[MyString]MyBytes
)
-var scalarProto2Type = pimpl.MessageInfo{GoType: reflect.TypeOf(new(ScalarProto2)), PBType: ptype.GoMessage(
- mustMakeMessageDesc(ptype.StandaloneMessage{
+var scalarProto2Type = pimpl.MessageInfo{GoType: reflect.TypeOf(new(ScalarProto2)), PBType: &prototype.Message{
+ MessageDescriptor: mustMakeMessageDesc(ptype.StandaloneMessage{
Syntax: pref.Proto2,
FullName: "ScalarProto2",
Fields: []ptype.Field{
@@ -219,10 +220,10 @@
{Name: "f22", Number: 22, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: V([]byte("22"))},
},
}),
- func(pref.MessageType) pref.Message {
+ NewMessage: func() pref.Message {
return new(ScalarProto2)
},
-)}
+}}
// TODO: Remove this.
func (m *ScalarProto2) Type() pref.MessageType { return scalarProto2Type.PBType }
@@ -304,8 +305,8 @@
MyBytesA MyString `protobuf:"22"`
}
-var scalarProto3Type = pimpl.MessageInfo{GoType: reflect.TypeOf(new(ScalarProto3)), PBType: ptype.GoMessage(
- mustMakeMessageDesc(ptype.StandaloneMessage{
+var scalarProto3Type = pimpl.MessageInfo{GoType: reflect.TypeOf(new(ScalarProto3)), PBType: &prototype.Message{
+ MessageDescriptor: mustMakeMessageDesc(ptype.StandaloneMessage{
Syntax: pref.Proto3,
FullName: "ScalarProto3",
Fields: []ptype.Field{
@@ -334,10 +335,10 @@
{Name: "f22", Number: 22, Cardinality: pref.Optional, Kind: pref.BytesKind},
},
}),
- func(pref.MessageType) pref.Message {
+ NewMessage: func() pref.Message {
return new(ScalarProto3)
},
-)}
+}}
// TODO: Remove this.
func (m *ScalarProto3) Type() pref.MessageType { return scalarProto3Type.PBType }
@@ -437,8 +438,8 @@
MyBytes4 ListStrings `protobuf:"19"`
}
-var listScalarsType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(ListScalars)), PBType: ptype.GoMessage(
- mustMakeMessageDesc(ptype.StandaloneMessage{
+var listScalarsType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(ListScalars)), PBType: &prototype.Message{
+ MessageDescriptor: mustMakeMessageDesc(ptype.StandaloneMessage{
Syntax: pref.Proto2,
FullName: "ListScalars",
Fields: []ptype.Field{
@@ -465,10 +466,10 @@
{Name: "f19", Number: 19, Cardinality: pref.Repeated, Kind: pref.BytesKind},
},
}),
- func(pref.MessageType) pref.Message {
+ NewMessage: func() pref.Message {
return new(ListScalars)
},
-)}
+}}
// TODO: Remove this.
func (m *ListScalars) Type() pref.MessageType { return listScalarsType.PBType }
@@ -628,8 +629,8 @@
}
}
-var mapScalarsType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(MapScalars)), PBType: ptype.GoMessage(
- mustMakeMessageDesc(ptype.StandaloneMessage{
+var mapScalarsType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(MapScalars)), PBType: &prototype.Message{
+ MessageDescriptor: mustMakeMessageDesc(ptype.StandaloneMessage{
Syntax: pref.Proto2,
FullName: "MapScalars",
Fields: []ptype.Field{
@@ -663,10 +664,10 @@
mustMakeMapEntry(25, pref.StringKind, pref.BytesKind),
},
}),
- func(pref.MessageType) pref.Message {
+ NewMessage: func() pref.Message {
return new(MapScalars)
},
-)}
+}}
// TODO: Remove this.
func (m *MapScalars) Type() pref.MessageType { return mapScalarsType.PBType }
@@ -807,8 +808,8 @@
Union isOneofScalars_Union `protobuf_oneof:"union"`
}
-var oneofScalarsType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(OneofScalars)), PBType: ptype.GoMessage(
- mustMakeMessageDesc(ptype.StandaloneMessage{
+var oneofScalarsType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(OneofScalars)), PBType: &prototype.Message{
+ MessageDescriptor: mustMakeMessageDesc(ptype.StandaloneMessage{
Syntax: pref.Proto2,
FullName: "OneofScalars",
Fields: []ptype.Field{
@@ -828,10 +829,10 @@
},
Oneofs: []ptype.Oneof{{Name: "union"}},
}),
- func(pref.MessageType) pref.Message {
+ NewMessage: func() pref.Message {
return new(OneofScalars)
},
-)}
+}}
// TODO: Remove this.
func (m *OneofScalars) Type() pref.MessageType { return oneofScalarsType.PBType }
@@ -983,16 +984,16 @@
type EnumProto2 int32
-var enumProto2Type = ptype.GoEnum(
- mustMakeEnumDesc(ptype.StandaloneEnum{
+var enumProto2Type = &prototype.Enum{
+ EnumDescriptor: mustMakeEnumDesc(ptype.StandaloneEnum{
Syntax: pref.Proto2,
FullName: "EnumProto2",
Values: []ptype.EnumValue{{Name: "DEAD", Number: 0xdead}, {Name: "BEEF", Number: 0xbeef}},
}),
- func(_ pref.EnumType, n pref.EnumNumber) pref.Enum {
+ NewEnum: func(n pref.EnumNumber) pref.Enum {
return EnumProto2(n)
},
-)
+}
// TODO: Remove this.
func (e EnumProto2) Type() pref.EnumType { return enumProto2Type }
@@ -1002,16 +1003,16 @@
type EnumProto3 int32
-var enumProto3Type = ptype.GoEnum(
- mustMakeEnumDesc(ptype.StandaloneEnum{
+var enumProto3Type = &prototype.Enum{
+ EnumDescriptor: mustMakeEnumDesc(ptype.StandaloneEnum{
Syntax: pref.Proto3,
FullName: "EnumProto3",
Values: []ptype.EnumValue{{Name: "ALPHA", Number: 0}, {Name: "BRAVO", Number: 1}},
}),
- func(_ pref.EnumType, n pref.EnumNumber) pref.Enum {
+ NewEnum: func(n pref.EnumNumber) pref.Enum {
return EnumProto3(n)
},
-)
+}
// TODO: Remove this.
func (e EnumProto3) Type() pref.EnumType { return enumProto3Type }
@@ -1031,8 +1032,8 @@
Union isEnumMessages_Union `protobuf_oneof:"union"`
}
-var enumMessagesType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(EnumMessages)), PBType: ptype.GoMessage(
- mustMakeMessageDesc(ptype.StandaloneMessage{
+var enumMessagesType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(EnumMessages)), PBType: &prototype.Message{
+ MessageDescriptor: mustMakeMessageDesc(ptype.StandaloneMessage{
Syntax: pref.Proto2,
FullName: "EnumMessages",
Fields: []ptype.Field{
@@ -1051,10 +1052,10 @@
},
Oneofs: []ptype.Oneof{{Name: "union"}},
}),
- func(pref.MessageType) pref.Message {
+ NewMessage: func() pref.Message {
return new(EnumMessages)
},
-)}
+}}
var enumMapDesc = mustMakeMessageDesc(ptype.StandaloneMessage{
Syntax: pref.Proto2,