reflect/protoreflect: add Descriptor specific methods
Added methods:
Enum.Descriptor
Message.Descriptor
EnumType.Descriptor
MessageType.Descriptor
ExtensionType.Descriptor
Message.New
All functionality is switched over to use those methods instead of
implicitly relying on the fact that {Enum,Message}Type implicitly
implement the associated descriptor interface.
This CL does not yet remove {Enum,Message}.Type or prevent
{Enum,Message,Extension}Type from implementating a descriptor.
That is a subsequent CL.
The Message.New method is also added to replace functionality
that will be lost when the Type methods are removed.
Change-Id: I7fefde1673bbd40bfdac489aca05cec9a6c98eb1
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/174918
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Herbie Ong <herbie@google.com>
diff --git a/internal/legacy/enum.go b/internal/legacy/enum.go
index 0ee397f..2a57b0b 100644
--- a/internal/legacy/enum.go
+++ b/internal/legacy/enum.go
@@ -55,12 +55,16 @@
goTyp reflect.Type
}
-func (e *enumWrapper) Number() pref.EnumNumber {
- return e.num
-}
+// TODO: Remove this.
func (e *enumWrapper) Type() pref.EnumType {
return e.pbTyp
}
+func (e *enumWrapper) Descriptor() pref.EnumDescriptor {
+ return e.pbTyp.Descriptor()
+}
+func (e *enumWrapper) Number() pref.EnumNumber {
+ return e.num
+}
func (e *enumWrapper) ProtoReflect() pref.Enum {
return e
}
diff --git a/internal/legacy/export.go b/internal/legacy/export.go
index 054108a..fa4375a 100644
--- a/internal/legacy/export.go
+++ b/internal/legacy/export.go
@@ -25,6 +25,10 @@
return loadEnumType(reflect.TypeOf(e))
}
+func (Export) EnumDescriptorOf(e interface{}) pref.EnumDescriptor {
+ return LoadEnumDesc(reflect.TypeOf(e))
+}
+
func (Export) MessageOf(m interface{}) pvalue.LegacyMessage {
return wrapMessage(reflect.ValueOf(m)).ProtoReflect().(pvalue.LegacyMessage)
}
@@ -33,8 +37,8 @@
return loadMessageType(reflect.TypeOf(m)).PBType
}
-func (Export) ExtensionTypeOf(d pref.ExtensionDescriptor, t interface{}) pref.ExtensionType {
- return extensionTypeOf(d, reflect.TypeOf(t))
+func (Export) MessageDescriptorOf(m interface{}) pref.MessageDescriptor {
+ return LoadMessageDesc(reflect.TypeOf(m))
}
func (Export) ExtensionDescFromType(t pref.ExtensionType) *piface.ExtensionDescV1 {
diff --git a/internal/legacy/extension.go b/internal/legacy/extension.go
index 94ad7b7..e9c8bb7 100644
--- a/internal/legacy/extension.go
+++ b/internal/legacy/extension.go
@@ -15,6 +15,7 @@
pfmt "github.com/golang/protobuf/v2/internal/typefmt"
pvalue "github.com/golang/protobuf/v2/internal/value"
pref "github.com/golang/protobuf/v2/reflect/protoreflect"
+ preg "github.com/golang/protobuf/v2/reflect/protoregistry"
piface "github.com/golang/protobuf/v2/runtime/protoiface"
)
@@ -46,25 +47,25 @@
// extensionDescFromType converts a v2 protoreflect.ExtensionType to a
// protoiface.ExtensionDescV1. The returned ExtensionDesc must not be mutated.
-func extensionDescFromType(t pref.ExtensionType) *piface.ExtensionDescV1 {
+func extensionDescFromType(xt pref.ExtensionType) *piface.ExtensionDescV1 {
// Fast-path: check whether an extension desc is already nested within.
- if t, ok := t.(interface {
+ if xt, ok := xt.(interface {
ProtoLegacyExtensionDesc() *piface.ExtensionDescV1
}); ok {
- if d := t.ProtoLegacyExtensionDesc(); d != nil {
+ if d := xt.ProtoLegacyExtensionDesc(); d != nil {
return d
}
}
// Fast-path: check the cache for whether this ExtensionType has already
// been converted to a legacy descriptor.
- if d, ok := extensionDescCache.Load(t); ok {
+ if d, ok := extensionDescCache.Load(xt); ok {
return d.(*piface.ExtensionDescV1)
}
// Determine the parent type if possible.
var parent piface.MessageV1
- if mt, ok := t.Extendee().(pref.MessageType); ok {
+ if mt, _ := preg.GlobalTypes.FindMessageByName(xt.Descriptor().Extendee().FullName()); mt != nil {
// Create a new parent message and unwrap it if possible.
mv := mt.New().Interface()
t := reflect.TypeOf(mv)
@@ -81,7 +82,7 @@
// Determine the v1 extension type, which is unfortunately not the same as
// the v2 ExtensionType.GoType.
- extType := t.GoType()
+ extType := xt.GoType()
switch extType.Kind() {
case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
extType = reflect.PtrTo(extType) // T -> *T for singular scalar fields
@@ -94,23 +95,21 @@
// Reconstruct the legacy enum full name, which is an odd mixture of the
// proto package name with the Go type name.
var enumName string
- if t.Kind() == pref.EnumKind {
+ if xt.Descriptor().Kind() == pref.EnumKind {
// Derive Go type name.
- // For legacy enums, unwrap the wrapper to get the underlying Go type.
- et := t.Enum().(pref.EnumType)
- var ev interface{} = et.New(0)
- if u, ok := ev.(pvalue.Unwrapper); ok {
- ev = u.ProtoUnwrap()
+ t := extType
+ if t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice {
+ t = t.Elem()
}
- enumName = reflect.TypeOf(ev).Name()
+ enumName = t.Name()
// Derive the proto package name.
// For legacy enums, obtain the proto package from the raw descriptor.
var protoPkg string
- if fd := parentFileDescriptor(et); fd != nil {
+ if fd := parentFileDescriptor(xt.Descriptor().Enum()); fd != nil {
protoPkg = string(fd.Package())
}
- if ed, ok := ev.(enumV1); ok && protoPkg == "" {
+ if ed, ok := reflect.Zero(t).Interface().(enumV1); ok && protoPkg == "" {
b, _ := ed.EnumDescriptor()
protoPkg = loadFileDesc(b).GetPackage()
}
@@ -122,21 +121,21 @@
// Derive the proto file that the extension was declared within.
var filename string
- if fd := parentFileDescriptor(t); fd != nil {
+ if fd := parentFileDescriptor(xt.Descriptor()); fd != nil {
filename = fd.Path()
}
// Construct and return a ExtensionDescV1.
d := &piface.ExtensionDescV1{
- Type: t,
+ Type: xt,
ExtendedType: parent,
ExtensionType: reflect.Zero(extType).Interface(),
- Field: int32(t.Number()),
- Name: string(t.FullName()),
- Tag: ptag.Marshal(t, enumName),
+ Field: int32(xt.Descriptor().Number()),
+ Name: string(xt.Descriptor().FullName()),
+ Tag: ptag.Marshal(xt.Descriptor(), enumName),
Filename: filename,
}
- if d, ok := extensionDescCache.LoadOrStore(t, d); ok {
+ if d, ok := extensionDescCache.LoadOrStore(xt, d); ok {
return d.(*piface.ExtensionDescV1)
}
return d
@@ -169,7 +168,15 @@
f := ptag.Unmarshal(d.Tag, t)
// Construct a v2 ExtensionType.
+ var ed pref.EnumDescriptor
+ var md pref.MessageDescriptor
conv := pvalue.NewLegacyConverter(t, f.Kind, Export{})
+ if conv.EnumType != nil {
+ ed = conv.EnumType.Descriptor()
+ }
+ if conv.MessageType != nil {
+ md = conv.MessageType.Descriptor()
+ }
xd, err := ptype.NewExtension(&ptype.StandaloneExtension{
FullName: pref.FullName(d.Name),
Number: pref.FieldNumber(d.Field),
@@ -177,19 +184,14 @@
Kind: f.Kind,
Default: f.Default,
Options: f.Options,
- EnumType: conv.EnumType,
- MessageType: conv.MessageType,
- ExtendedType: pimpl.Export{}.MessageTypeOf(d.ExtendedType),
+ EnumType: ed,
+ MessageType: md,
+ ExtendedType: pimpl.Export{}.MessageDescriptorOf(d.ExtendedType),
})
if err != nil {
panic(err)
}
- var zv interface{}
- switch xd.Kind() {
- case pref.EnumKind, pref.MessageKind, pref.GroupKind:
- zv = reflect.Zero(t).Interface()
- }
- xt := pimpl.Export{}.ExtensionTypeOf(xd, zv)
+ xt := ExtensionTypeOf(xd, t)
// Cache the conversion for both directions.
extensionDescCache.LoadOrStore(xt, d)
@@ -199,17 +201,26 @@
return xt
}
-// extensionTypeOf returns a protoreflect.ExtensionType where the GoType
-// is the underlying v1 Go type instead of the wrapper types used to present
-// v1 Go types as if they satisfied the v2 API.
+// ExtensionTypeOf returns a protoreflect.ExtensionType where the type of the
+// field is t. The type t must be provided if the field is an enum or message.
//
-// This function is only valid if xd.Kind is an enum or message.
-func extensionTypeOf(xd pref.ExtensionDescriptor, t reflect.Type) pref.ExtensionType {
- // Step 1: Create an ExtensionType where GoType is the wrapper type.
+// This is exported for testing purposes.
+func ExtensionTypeOf(xd pref.ExtensionDescriptor, t reflect.Type) pref.ExtensionType {
+ // Extension types for non-enums and non-messages are simple.
+ switch xd.Kind() {
+ case pref.EnumKind, pref.MessageKind, pref.GroupKind:
+ default:
+ return ptype.GoExtension(xd, nil, nil)
+ }
+
+ // Create an ExtensionType where GoType is the wrapper type.
conv := pvalue.NewLegacyConverter(t, xd.Kind(), Export{})
xt := ptype.GoExtension(xd, conv.EnumType, conv.MessageType)
+ if !conv.IsLegacy {
+ return xt
+ }
- // Step 2: Wrap ExtensionType such that GoType presents the legacy Go type.
+ // Wrap ExtensionType such that GoType presents the legacy Go type.
xt2 := &extensionType{ExtensionType: xt}
if xd.Cardinality() != pref.Repeated {
xt2.typ = t
@@ -264,4 +275,4 @@
func (x *extensionType) New() pref.Value { return x.new() }
func (x *extensionType) ValueOf(v interface{}) pref.Value { return x.valueOf(v) }
func (x *extensionType) InterfaceOf(v pref.Value) interface{} { return x.interfaceOf(v) }
-func (x *extensionType) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, x) }
+func (x *extensionType) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, x.Descriptor()) }
diff --git a/internal/legacy/extension_test.go b/internal/legacy/extension_test.go
index 630c158..cdba627 100644
--- a/internal/legacy/extension_test.go
+++ b/internal/legacy/extension_test.go
@@ -5,17 +5,15 @@
package legacy_test
import (
+ "reflect"
"testing"
pimpl "github.com/golang/protobuf/v2/internal/impl"
+ plegacy "github.com/golang/protobuf/v2/internal/legacy"
ptype "github.com/golang/protobuf/v2/internal/prototype"
pref "github.com/golang/protobuf/v2/reflect/protoreflect"
piface "github.com/golang/protobuf/v2/runtime/protoiface"
- // The legacy package must be imported prior to use of any legacy messages.
- // TODO: Remove this when protoV1 registers these hooks for you.
- plegacy "github.com/golang/protobuf/v2/internal/legacy"
-
proto2_20180125 "github.com/golang/protobuf/v2/internal/testprotos/legacy/proto2.v1.0.0-20180125-92554152"
)
@@ -34,22 +32,22 @@
func mustMakeExtensionType(x *ptype.StandaloneExtension, v interface{}) pref.ExtensionType {
xd, err := ptype.NewExtension(x)
if err != nil {
- panic(xd)
+ panic(err)
}
- return pimpl.Export{}.ExtensionTypeOf(xd, v)
+ return plegacy.ExtensionTypeOf(xd, reflect.TypeOf(v))
}
var (
- parentType = pimpl.Export{}.MessageTypeOf((*legacyTestMessage)(nil))
- messageV1Type = pimpl.Export{}.MessageTypeOf((*proto2_20180125.Message_ChildMessage)(nil))
+ parentDesc = pimpl.Export{}.MessageDescriptorOf((*legacyTestMessage)(nil))
+ messageV1Desc = pimpl.Export{}.MessageDescriptorOf((*proto2_20180125.Message_ChildMessage)(nil))
wantType = mustMakeExtensionType(&ptype.StandaloneExtension{
FullName: "fizz.buzz.optional_message_v1",
Number: 10007,
Cardinality: pref.Optional,
Kind: pref.MessageKind,
- MessageType: messageV1Type,
- ExtendedType: parentType,
+ MessageType: messageV1Desc,
+ ExtendedType: parentDesc,
}, (*proto2_20180125.Message_ChildMessage)(nil))
wantDesc = &piface.ExtensionDescV1{
ExtendedType: (*legacyTestMessage)(nil),
diff --git a/internal/legacy/legacy_test.go b/internal/legacy/legacy_test.go
index 3bac3b3..9bb484c 100644
--- a/internal/legacy/legacy_test.go
+++ b/internal/legacy/legacy_test.go
@@ -31,7 +31,7 @@
const numParallel = 5
var messageATypes [numParallel]protoreflect.MessageType
var messageBTypes [numParallel]protoreflect.MessageType
- var enumTypes [numParallel]protoreflect.EnumType
+ var enumDescs [numParallel]protoreflect.EnumDescriptor
// Concurrently load message and enum types.
var wg sync.WaitGroup
@@ -48,31 +48,30 @@
}()
go func() {
defer wg.Done()
- enumTypes[i] = Export{}.EnumTypeOf(Enum(0))
+ enumDescs[i] = Export{}.EnumDescriptorOf(Enum(0))
}()
}
wg.Wait()
var (
wantMTA = messageATypes[0]
- wantMDA = messageATypes[0].Fields().ByNumber(1).Message()
+ wantMDA = messageATypes[0].Descriptor().Fields().ByNumber(1).Message()
wantMTB = messageBTypes[0]
- wantMDB = messageBTypes[0].Fields().ByNumber(2).Message()
- wantET = enumTypes[0]
- wantED = messageATypes[0].Fields().ByNumber(3).Enum()
+ wantMDB = messageBTypes[0].Descriptor().Fields().ByNumber(2).Message()
+ wantED = messageATypes[0].Descriptor().Fields().ByNumber(3).Enum()
)
for _, gotMT := range messageATypes[1:] {
if gotMT != wantMTA {
t.Error("MessageType(MessageA) mismatch")
}
- if gotMDA := gotMT.Fields().ByNumber(1).Message(); gotMDA != wantMDA {
+ if gotMDA := gotMT.Descriptor().Fields().ByNumber(1).Message(); gotMDA != wantMDA {
t.Error("MessageDescriptor(MessageA) mismatch")
}
- if gotMDB := gotMT.Fields().ByNumber(2).Message(); gotMDB != wantMDB {
+ if gotMDB := gotMT.Descriptor().Fields().ByNumber(2).Message(); gotMDB != wantMDB {
t.Error("MessageDescriptor(MessageB) mismatch")
}
- if gotED := gotMT.Fields().ByNumber(3).Enum(); gotED != wantED {
+ if gotED := gotMT.Descriptor().Fields().ByNumber(3).Enum(); gotED != wantED {
t.Error("EnumDescriptor(Enum) mismatch")
}
}
@@ -80,18 +79,18 @@
if gotMT != wantMTB {
t.Error("MessageType(MessageB) mismatch")
}
- if gotMDA := gotMT.Fields().ByNumber(1).Message(); gotMDA != wantMDA {
+ if gotMDA := gotMT.Descriptor().Fields().ByNumber(1).Message(); gotMDA != wantMDA {
t.Error("MessageDescriptor(MessageA) mismatch")
}
- if gotMDB := gotMT.Fields().ByNumber(2).Message(); gotMDB != wantMDB {
+ if gotMDB := gotMT.Descriptor().Fields().ByNumber(2).Message(); gotMDB != wantMDB {
t.Error("MessageDescriptor(MessageB) mismatch")
}
- if gotED := gotMT.Fields().ByNumber(3).Enum(); gotED != wantED {
+ if gotED := gotMT.Descriptor().Fields().ByNumber(3).Enum(); gotED != wantED {
t.Error("EnumDescriptor(Enum) mismatch")
}
}
- for _, gotET := range enumTypes[1:] {
- if gotET != wantET {
+ for _, gotED := range enumDescs[1:] {
+ if gotED != wantED {
t.Error("EnumType(Enum) mismatch")
}
}
diff --git a/internal/legacy/message.go b/internal/legacy/message.go
index 01bc053..ee242ca 100644
--- a/internal/legacy/message.go
+++ b/internal/legacy/message.go
@@ -232,14 +232,14 @@
// Populate EnumType and MessageType.
if f.EnumType == nil && f.Kind == pref.EnumKind {
if ev, ok := reflect.Zero(t).Interface().(pref.Enum); ok {
- f.EnumType = ev.Type()
+ f.EnumType = ev.Descriptor()
} else {
f.EnumType = LoadEnumDesc(t)
}
}
if f.MessageType == nil && (f.Kind == pref.MessageKind || f.Kind == pref.GroupKind) {
if mv, ok := reflect.Zero(t).Interface().(pref.ProtoMessage); ok {
- f.MessageType = mv.ProtoReflect().Type()
+ f.MessageType = mv.ProtoReflect().Descriptor()
} else if t.Kind() == reflect.Map {
m := &ptype.StandaloneMessage{
Syntax: parent.Syntax,