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/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()) }