From 1605775be0bfd4084c40cac4262ada8900791de1 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Mon, 11 Nov 2019 16:30:04 -0800 Subject: [PATCH] internal/impl: handle some dynamic legacy messages When creating a MessageDescriptor for a legacy message with a Descriptor method, we call that method on the type's zero value to get the message's DescriptorProto. Some existing dynamic message types have a Descriptor method which panics in this case. Catch the panic and continue as if the Descriptor method wasn't present. Change-Id: I98d4625d6917cc1ec25737e5670a443f5d02a404 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/206637 Reviewed-by: Joe Tsai --- internal/impl/legacy_message.go | 15 ++++++++++++++- proto/methods_test.go | 21 +++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/internal/impl/legacy_message.go b/internal/impl/legacy_message.go index e078045f..3d0a9211 100644 --- a/internal/impl/legacy_message.go +++ b/internal/impl/legacy_message.go @@ -93,7 +93,20 @@ func legacyLoadMessageDesc(t reflect.Type, name pref.FullName) pref.MessageDescr if !ok { return aberrantLoadMessageDesc(t, name) } - b, idxs := mdV1.Descriptor() + + // If this is a dynamic message type where there isn't a 1-1 mapping between + // Go and protobuf types, calling the Descriptor method on the zero value of + // the message type isn't likely to work. If it panics, swallow the panic and + // continue as if the Descriptor method wasn't present. + b, idxs := func() ([]byte, []int) { + defer func() { + recover() + }() + return mdV1.Descriptor() + }() + if b == nil { + return aberrantLoadMessageDesc(t, name) + } md := legacyLoadFileDesc(b).Messages().Get(idxs[0]) for _, i := range idxs[1:] { diff --git a/proto/methods_test.go b/proto/methods_test.go index cc5dfcec..385fcbf6 100644 --- a/proto/methods_test.go +++ b/proto/methods_test.go @@ -79,3 +79,24 @@ func TestLegacyUnmarshalMethod(t *testing.T) { t.Fatalf("proto.Unmarshal(selfMarshaler{}): Marshal method not called") } } + +type descPanicSelfMarshaler struct{} + +const descPanicSelfMarshalerBytes = "bytes" + +func (m descPanicSelfMarshaler) Reset() {} +func (m descPanicSelfMarshaler) ProtoMessage() {} +func (m descPanicSelfMarshaler) Descriptor() ([]byte, []int) { panic("Descriptor method panics") } +func (m descPanicSelfMarshaler) String() string { return "descPanicSelfMarshaler{}" } +func (m descPanicSelfMarshaler) Marshal() ([]byte, error) { + return []byte(descPanicSelfMarshalerBytes), nil +} + +func TestSelfMarshalerDescriptorPanics(t *testing.T) { + m := descPanicSelfMarshaler{} + got, err := proto.Marshal(impl.Export{}.MessageOf(m).Interface()) + want := []byte(descPanicSelfMarshalerBytes) + if err != nil || !bytes.Equal(got, want) { + t.Fatalf("proto.Marshal(%v) = %v, %v; want %v, nil", m, got, err, want) + } +}