internal/impl: handle legacy ExtensionDesc form

Add support for an ExtensionDesc with only Field populated as returned by
protoV1.ExtensionDescs. Rather than panicking when TypeDescriptor is called,
return a placeholder that preserves the name and/or number.

Change-Id: I60352a7aec8ccd8a0c1fb08db5891043a441695f
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/193725
Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
Joe Tsai 2019-09-05 17:25:15 -07:00
parent 6e095998ae
commit bee625258d
3 changed files with 85 additions and 4 deletions

View File

@ -140,11 +140,13 @@ func (xi *ExtensionInfo) lazyInitSlow() {
if xi.desc.ExtensionDescriptor == nil { if xi.desc.ExtensionDescriptor == nil {
xi.initFromLegacy() xi.initFromLegacy()
} }
if !xi.desc.ExtensionDescriptor.IsPlaceholder() {
if xi.ExtensionType == nil { if xi.ExtensionType == nil {
xi.initToLegacy() xi.initToLegacy()
} }
xi.conv = NewConverter(xi.goType, xi.desc) xi.conv = NewConverter(xi.goType, xi.desc)
} }
}
type extensionTypeDescriptor struct { type extensionTypeDescriptor struct {
pref.ExtensionDescriptor pref.ExtensionDescriptor

View File

@ -180,7 +180,7 @@ func (OneofMessage) isOneofUnion() {}
type AberrantEnum int32 type AberrantEnum int32
func TestAberrant(t *testing.T) { func TestAberrantMessages(t *testing.T) {
want := new(descriptorpb.DescriptorProto) want := new(descriptorpb.DescriptorProto)
if err := prototext.Unmarshal([]byte(` if err := prototext.Unmarshal([]byte(`
name: "AberrantMessage" name: "AberrantMessage"
@ -320,3 +320,34 @@ func TestAberrantRace(t *testing.T) {
t.Errorf("mismatching exact message descriptors") t.Errorf("mismatching exact message descriptors")
} }
} }
func TestAberrantExtensions(t *testing.T) {
tests := []struct {
in *impl.ExtensionInfo
wantName protoreflect.FullName
wantNumber protoreflect.FieldNumber
wantPlaceholder bool
}{{
in: &impl.ExtensionInfo{Field: 500},
wantNumber: 500,
wantPlaceholder: true,
}, {
in: &impl.ExtensionInfo{Name: "foo.bar.baz"},
wantName: "foo.bar.baz",
wantPlaceholder: true,
}}
for _, tt := range tests {
t.Run("", func(t *testing.T) {
xtd := tt.in.TypeDescriptor()
switch {
case xtd.FullName() != tt.wantName:
t.Errorf("FullName() = %v, want %v", xtd.FullName(), tt.wantName)
case xtd.Number() != tt.wantNumber:
t.Errorf("Number() = %v, want %v", xtd.Number(), tt.wantNumber)
case xtd.IsPlaceholder() != tt.wantPlaceholder:
t.Errorf("IsPlaceholder() = %v, want %v", xtd.IsPlaceholder(), tt.wantPlaceholder)
}
})
}
}

View File

@ -7,9 +7,11 @@ package impl
import ( import (
"reflect" "reflect"
"google.golang.org/protobuf/internal/descopts"
"google.golang.org/protobuf/internal/encoding/messageset" "google.golang.org/protobuf/internal/encoding/messageset"
ptag "google.golang.org/protobuf/internal/encoding/tag" ptag "google.golang.org/protobuf/internal/encoding/tag"
"google.golang.org/protobuf/internal/filedesc" "google.golang.org/protobuf/internal/filedesc"
"google.golang.org/protobuf/internal/pragma"
pref "google.golang.org/protobuf/reflect/protoreflect" pref "google.golang.org/protobuf/reflect/protoreflect"
preg "google.golang.org/protobuf/reflect/protoregistry" preg "google.golang.org/protobuf/reflect/protoregistry"
piface "google.golang.org/protobuf/runtime/protoiface" piface "google.golang.org/protobuf/runtime/protoiface"
@ -71,6 +73,17 @@ func (xi *ExtensionInfo) initToLegacy() {
// initFromLegacy initializes an ExtensionInfo from // initFromLegacy initializes an ExtensionInfo from
// the contents of the deprecated exported fields of the type. // the contents of the deprecated exported fields of the type.
func (xi *ExtensionInfo) initFromLegacy() { func (xi *ExtensionInfo) initFromLegacy() {
// The v1 API returns "type incomplete" descriptors where only the
// field number is specified. In such a case, use a placeholder.
if xi.ExtendedType == nil || xi.ExtensionType == nil {
xd := placeholderExtension{
name: pref.FullName(xi.Name),
number: pref.FieldNumber(xi.Field),
}
xi.desc = extensionTypeDescriptor{xd, xi}
return
}
// Resolve enum or message dependencies. // Resolve enum or message dependencies.
var ed pref.EnumDescriptor var ed pref.EnumDescriptor
var md pref.MessageDescriptor var md pref.MessageDescriptor
@ -123,3 +136,38 @@ func (xi *ExtensionInfo) initFromLegacy() {
xi.goType = tt xi.goType = tt
xi.desc = extensionTypeDescriptor{xd, xi} xi.desc = extensionTypeDescriptor{xd, xi}
} }
type placeholderExtension struct {
name pref.FullName
number pref.FieldNumber
}
func (x placeholderExtension) ParentFile() pref.FileDescriptor { return nil }
func (x placeholderExtension) Parent() pref.Descriptor { return nil }
func (x placeholderExtension) Index() int { return 0 }
func (x placeholderExtension) Syntax() pref.Syntax { return 0 }
func (x placeholderExtension) Name() pref.Name { return x.name.Name() }
func (x placeholderExtension) FullName() pref.FullName { return x.name }
func (x placeholderExtension) IsPlaceholder() bool { return true }
func (x placeholderExtension) Options() pref.ProtoMessage { return descopts.Field }
func (x placeholderExtension) Number() pref.FieldNumber { return x.number }
func (x placeholderExtension) Cardinality() pref.Cardinality { return 0 }
func (x placeholderExtension) Kind() pref.Kind { return 0 }
func (x placeholderExtension) HasJSONName() bool { return false }
func (x placeholderExtension) JSONName() string { return "" }
func (x placeholderExtension) IsExtension() bool { return true }
func (x placeholderExtension) IsWeak() bool { return false }
func (x placeholderExtension) IsPacked() bool { return false }
func (x placeholderExtension) IsList() bool { return false }
func (x placeholderExtension) IsMap() bool { return false }
func (x placeholderExtension) MapKey() pref.FieldDescriptor { return nil }
func (x placeholderExtension) MapValue() pref.FieldDescriptor { return nil }
func (x placeholderExtension) HasDefault() bool { return false }
func (x placeholderExtension) Default() pref.Value { return pref.Value{} }
func (x placeholderExtension) DefaultEnumValue() pref.EnumValueDescriptor { return nil }
func (x placeholderExtension) ContainingOneof() pref.OneofDescriptor { return nil }
func (x placeholderExtension) ContainingMessage() pref.MessageDescriptor { return nil }
func (x placeholderExtension) Enum() pref.EnumDescriptor { return nil }
func (x placeholderExtension) Message() pref.MessageDescriptor { return nil }
func (x placeholderExtension) ProtoType(pref.FieldDescriptor) { return }
func (x placeholderExtension) ProtoInternal(pragma.DoNotImplement) { return }