internal/filedesc, internal/filetype: initial commit
The internal/fileinit package is split apart into two packages:
* internal/filedesc constructs descriptors from the raw proto.
It is very similar to the previous internal/fileinit package.
* internal/filetype wraps descriptors with Go type information
Overview:
* The internal/fileinit package will be deleted in a future CL.
It is kept around since the v1 repo currently depends on it.
* The internal/prototype package is deleted. All former usages of it
are now using internal/filedesc instead. Most significantly,
the reflect/protodesc package was almost entirely re-written.
* The internal/impl package drops support for messages that do not
have a Descriptor method (pre-2016). This removes a significant amount
of technical debt.
filedesc.Builder to parse raw descriptors.
* The internal/encoding/defval package now handles enum values by name.
Change-Id: I3957bcc8588a70470fd6c7de1122216b80615ab7
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/182360
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/filedesc/build.go b/internal/filedesc/build.go
new file mode 100644
index 0000000..28bd370
--- /dev/null
+++ b/internal/filedesc/build.go
@@ -0,0 +1,150 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package filedesc provides functionality for constructing descriptors.
+package filedesc
+
+import (
+ "google.golang.org/protobuf/internal/encoding/wire"
+ "google.golang.org/protobuf/internal/fieldnum"
+ "google.golang.org/protobuf/reflect/protoreflect"
+ pref "google.golang.org/protobuf/reflect/protoreflect"
+ preg "google.golang.org/protobuf/reflect/protoregistry"
+)
+
+// DescBuilder construct a protoreflect.FileDescriptor from the raw descriptor.
+type DescBuilder struct {
+ // RawDescriptor is the wire-encoded bytes of FileDescriptorProto
+ // and must be populated.
+ RawDescriptor []byte
+
+ // NumEnums is the total number of enums declared in the file.
+ NumEnums int32
+ // NumMessages is the total number of messages declared in the file.
+ // It includes the implicit message declarations for map entries.
+ NumMessages int32
+ // NumExtensions is the total number of extensions declared in the file.
+ NumExtensions int32
+ // NumServices is the total number of services declared in the file.
+ NumServices int32
+
+ // TypeResolver resolves extension field types for descriptor options.
+ // If nil, it uses protoregistry.GlobalTypes.
+ TypeResolver interface {
+ preg.ExtensionTypeResolver
+ }
+
+ // FileRegistry is use to lookup file, enum, and message dependencies.
+ // Once constructed, the file descriptor is registered here.
+ // If nil, it uses protoregistry.GlobalFiles.
+ FileRegistry interface {
+ FindFileByPath(string) (protoreflect.FileDescriptor, error)
+ FindEnumByName(pref.FullName) (pref.EnumDescriptor, error)
+ FindMessageByName(pref.FullName) (pref.MessageDescriptor, error)
+ Register(...pref.FileDescriptor) error
+ }
+}
+
+// resolverByIndex is an interface DescBuilder.FileRegistry may implement.
+// If so, it permits looking up an enum or message dependency based on the
+// sub-list and element index into filetype.TypeBuilder.DependencyIndexes.
+type resolverByIndex interface {
+ FindEnumByIndex(int32, int32, []Enum, []Message) pref.EnumDescriptor
+ FindMessageByIndex(int32, int32, []Enum, []Message) pref.MessageDescriptor
+}
+
+// Indexes of each sub-list in filetype.TypeBuilder.DependencyIndexes.
+const (
+ listFieldDeps int32 = iota
+ listExtTargets
+ listExtDeps
+ listMethInDeps
+ listMethOutDeps
+)
+
+// Build constructs a FileDescriptor given the parameters set in DescBuilder.
+// It assumes that the inputs are well-formed and panics if any inconsistencies
+// are encountered.
+//
+// If NumEnums+NumMessages+NumExtensions+NumServices is zero,
+// then Build automatically derives them from the raw descriptor.
+func (db DescBuilder) Build() (out struct {
+ File pref.FileDescriptor
+
+ // Enums is all enum descriptors in "flattened ordering".
+ Enums []Enum
+ // Messages is all message descriptors in "flattened ordering".
+ // It includes the implicit message declarations for map entries.
+ Messages []Message
+ // Extensions is all extension descriptors in "flattened ordering".
+ Extensions []Extension
+ // Service is all service descriptors in "flattened ordering".
+ Services []Service
+}) {
+ // Populate the counts if uninitialized.
+ if db.NumEnums+db.NumMessages+db.NumExtensions+db.NumServices == 0 {
+ db.unmarshalCounts(db.RawDescriptor, true)
+ }
+
+ // Initialize resolvers and registries if unpopulated.
+ if db.TypeResolver == nil {
+ db.TypeResolver = preg.GlobalTypes
+ }
+ if db.FileRegistry == nil {
+ db.FileRegistry = preg.GlobalFiles
+ }
+
+ fd := newRawFile(db)
+ out.File = fd
+ out.Enums = fd.allEnums
+ out.Messages = fd.allMessages
+ out.Extensions = fd.allExtensions
+ out.Services = fd.allServices
+
+ if err := db.FileRegistry.Register(fd); err != nil {
+ panic(err)
+ }
+ return out
+}
+
+// unmarshalCounts counts the number of enum, message, extension, and service
+// declarations in the raw message, which is either a FileDescriptorProto
+// or a MessageDescriptorProto depending on whether isFile is set.
+func (db *DescBuilder) unmarshalCounts(b []byte, isFile bool) {
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.BytesType:
+ v, m := wire.ConsumeBytes(b)
+ b = b[m:]
+ if isFile {
+ switch num {
+ case fieldnum.FileDescriptorProto_EnumType:
+ db.NumEnums++
+ case fieldnum.FileDescriptorProto_MessageType:
+ db.unmarshalCounts(v, false)
+ db.NumMessages++
+ case fieldnum.FileDescriptorProto_Extension:
+ db.NumExtensions++
+ case fieldnum.FileDescriptorProto_Service:
+ db.NumServices++
+ }
+ } else {
+ switch num {
+ case fieldnum.DescriptorProto_EnumType:
+ db.NumEnums++
+ case fieldnum.DescriptorProto_NestedType:
+ db.unmarshalCounts(v, false)
+ db.NumMessages++
+ case fieldnum.DescriptorProto_Extension:
+ db.NumExtensions++
+ }
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+}
diff --git a/internal/filedesc/build_test.go b/internal/filedesc/build_test.go
new file mode 100644
index 0000000..edeb56e
--- /dev/null
+++ b/internal/filedesc/build_test.go
@@ -0,0 +1,117 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package filedesc_test
+
+import (
+ "bytes"
+ "compress/gzip"
+ "io/ioutil"
+ "testing"
+
+ proto "github.com/golang/protobuf/proto"
+ "google.golang.org/protobuf/reflect/protodesc"
+ "google.golang.org/protobuf/reflect/protoreflect"
+
+ testpb "google.golang.org/protobuf/internal/testprotos/test"
+ "google.golang.org/protobuf/types/descriptorpb"
+)
+
+func TestInit(t *testing.T) {
+ // Compare the FileDescriptorProto for the same test file from two different sources:
+ //
+ // 1. The result of passing the filedesc-produced FileDescriptor through protodesc.
+ // 2. The protoc-generated wire-encoded message.
+ //
+ // This serves as a test of both filedesc and protodesc.
+ got := protodesc.ToFileDescriptorProto(testpb.File_test_test_proto)
+
+ want := &descriptorpb.FileDescriptorProto{}
+ zb, _ := (&testpb.TestAllTypes{}).Descriptor()
+ r, _ := gzip.NewReader(bytes.NewBuffer(zb))
+ b, _ := ioutil.ReadAll(r)
+ if err := proto.Unmarshal(b, want); err != nil {
+ t.Fatal(err)
+ }
+
+ if !proto.Equal(got, want) {
+ t.Errorf("protodesc.ToFileDescriptorProto(testpb.Test_protoFile) is not equal to the protoc-generated FileDescriptorProto for internal/testprotos/test/test.proto")
+ }
+
+ // Verify that the test proto file provides exhaustive coverage of all descriptor fields.
+ seen := make(map[protoreflect.FullName]bool)
+ visitFields(want.ProtoReflect(), func(field protoreflect.FieldDescriptor) {
+ seen[field.FullName()] = true
+ })
+ ignore := map[protoreflect.FullName]bool{
+ // The protoreflect descriptors don't include source info.
+ "google.protobuf.FileDescriptorProto.source_code_info": true,
+ "google.protobuf.FileDescriptorProto.syntax": true,
+
+ // TODO: Test oneof and extension options. Testing these requires extending the
+ // options messages (because they contain no user-settable fields), but importing
+ // decriptor.proto from test.proto currently causes an import cycle. Add test
+ // cases when that import cycle has been fixed.
+ "google.protobuf.OneofDescriptorProto.options": true,
+ }
+ for _, messageName := range []protoreflect.Name{
+ "FileDescriptorProto",
+ "DescriptorProto",
+ "FieldDescriptorProto",
+ "OneofDescriptorProto",
+ "EnumDescriptorProto",
+ "EnumValueDescriptorProto",
+ "ServiceDescriptorProto",
+ "MethodDescriptorProto",
+ } {
+ message := descriptorpb.File_google_protobuf_descriptor_proto.Messages().ByName(messageName)
+ for i, fields := 0, message.Fields(); i < fields.Len(); i++ {
+ if name := fields.Get(i).FullName(); !seen[name] && !ignore[name] {
+ t.Errorf("No test for descriptor field: %v", name)
+ }
+ }
+ }
+
+ // Verify that message descriptors for map entries have no Go type info.
+ mapEntryName := protoreflect.FullName("goproto.proto.test.TestAllTypes.MapInt32Int32Entry")
+ d := testpb.File_test_test_proto.Messages().ByName("TestAllTypes").Fields().ByName("map_int32_int32").Message()
+ if gotName, wantName := d.FullName(), mapEntryName; gotName != wantName {
+ t.Fatalf("looked up wrong descriptor: got %v, want %v", gotName, wantName)
+ }
+ if _, ok := d.(protoreflect.MessageType); ok {
+ t.Errorf("message descriptor for %v must not implement protoreflect.MessageType", mapEntryName)
+ }
+}
+
+// visitFields calls f for every field set in m and its children.
+func visitFields(m protoreflect.Message, f func(protoreflect.FieldDescriptor)) {
+ m.Range(func(fd protoreflect.FieldDescriptor, value protoreflect.Value) bool {
+ f(fd)
+ switch fd.Kind() {
+ case protoreflect.MessageKind, protoreflect.GroupKind:
+ if fd.IsList() {
+ for i, list := 0, value.List(); i < list.Len(); i++ {
+ visitFields(list.Get(i).Message(), f)
+ }
+ } else {
+ visitFields(value.Message(), f)
+ }
+ }
+ return true
+ })
+}
+
+func TestWeakInit(t *testing.T) {
+ file := testpb.File_test_test_proto
+ fd := file.Messages().ByName("TestWeak").Fields().ByName("weak_message")
+ if want, got := fd.IsWeak(), true; got != want {
+ t.Errorf("field %v: IsWeak() = %v, want %v", fd.FullName(), want, got)
+ }
+ if want, got := fd.Message().IsPlaceholder(), false; got != want {
+ t.Errorf("field %v: Message.IsPlaceholder() = %v, want %v", fd.FullName(), want, got)
+ }
+ if fd.Message().Fields().Len() == 0 {
+ t.Errorf("field %v: Message().Fields().Len() == 0, want >0", fd.FullName())
+ }
+}
diff --git a/internal/filedesc/desc.go b/internal/filedesc/desc.go
new file mode 100644
index 0000000..40c47dc
--- /dev/null
+++ b/internal/filedesc/desc.go
@@ -0,0 +1,555 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package filedesc
+
+import (
+ "bytes"
+ "fmt"
+ "sync"
+ "sync/atomic"
+
+ "google.golang.org/protobuf/internal/descfmt"
+ "google.golang.org/protobuf/internal/descopts"
+ "google.golang.org/protobuf/internal/encoding/defval"
+ "google.golang.org/protobuf/internal/pragma"
+ pref "google.golang.org/protobuf/reflect/protoreflect"
+)
+
+// The types in this file may have a suffix:
+// • L0: Contains fields common to all descriptors (except File) and
+// must be initialized up front.
+// • L1: Contains fields specific to a descriptor and
+// must be initialized up front.
+// • L2: Contains fields that are lazily initialized when constructing
+// from the raw file descriptor. When constructing as a literal, the L2
+// fields must be initialized up front.
+//
+// The types are exported so that packages like reflect/protodesc can
+// directly construct descriptors.
+
+type (
+ File struct {
+ fileRaw
+ L1 FileL1
+
+ once uint32 // atomically set if L2 is valid
+ mu sync.Mutex // protects L2
+ L2 *FileL2
+ }
+ FileL1 struct {
+ Syntax pref.Syntax
+ Path string
+ Package pref.FullName
+
+ Enums Enums
+ Messages Messages
+ Extensions Extensions
+ Services Services
+ }
+ FileL2 struct {
+ Options func() pref.ProtoMessage
+ Imports FileImports
+ }
+)
+
+func (fd *File) ParentFile() pref.FileDescriptor { return fd }
+func (fd *File) Parent() pref.Descriptor { return nil }
+func (fd *File) Index() int { return 0 }
+func (fd *File) Syntax() pref.Syntax { return fd.L1.Syntax }
+func (fd *File) Name() pref.Name { return fd.L1.Package.Name() }
+func (fd *File) FullName() pref.FullName { return fd.L1.Package }
+func (fd *File) IsPlaceholder() bool { return false }
+func (fd *File) Options() pref.ProtoMessage {
+ if f := fd.lazyInit().Options; f != nil {
+ return f()
+ }
+ return descopts.File
+}
+func (fd *File) Path() string { return fd.L1.Path }
+func (fd *File) Package() pref.FullName { return fd.L1.Package }
+func (fd *File) Imports() pref.FileImports { return &fd.lazyInit().Imports }
+func (fd *File) Enums() pref.EnumDescriptors { return &fd.L1.Enums }
+func (fd *File) Messages() pref.MessageDescriptors { return &fd.L1.Messages }
+func (fd *File) Extensions() pref.ExtensionDescriptors { return &fd.L1.Extensions }
+func (fd *File) Services() pref.ServiceDescriptors { return &fd.L1.Services }
+func (fd *File) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, fd) }
+func (fd *File) ProtoType(pref.FileDescriptor) {}
+func (fd *File) ProtoInternal(pragma.DoNotImplement) {}
+
+func (fd *File) lazyInit() *FileL2 {
+ if atomic.LoadUint32(&fd.once) == 0 {
+ fd.lazyInitOnce()
+ }
+ return fd.L2
+}
+
+func (fd *File) lazyInitOnce() {
+ fd.mu.Lock()
+ if fd.L2 == nil {
+ fd.lazyRawInit() // recursively initializes all L2 structures
+ }
+ atomic.StoreUint32(&fd.once, 1)
+ fd.mu.Unlock()
+}
+
+// ProtoLegacyRawDesc is a pseudo-internal API for allowing the v1 code
+// to be able to retrieve the raw descriptor.
+//
+// WARNING: This method is exempt from the compatibility promise and may be
+// removed in the future without warning.
+func (fd *File) ProtoLegacyRawDesc() []byte {
+ return fd.builder.RawDescriptor
+}
+
+type (
+ Enum struct {
+ Base
+ L1 EnumL1
+ L2 *EnumL2 // protected by fileDesc.once
+ }
+ EnumL1 struct{}
+ EnumL2 struct {
+ Options func() pref.ProtoMessage
+ Values EnumValues
+ ReservedNames Names
+ ReservedRanges EnumRanges
+ }
+
+ EnumValue struct {
+ Base
+ L1 EnumValueL1
+ }
+ EnumValueL1 struct {
+ Options func() pref.ProtoMessage
+ Number pref.EnumNumber
+ }
+)
+
+func (ed *Enum) Options() pref.ProtoMessage {
+ if f := ed.lazyInit().Options; f != nil {
+ return f()
+ }
+ return descopts.Enum
+}
+func (ed *Enum) Values() pref.EnumValueDescriptors { return &ed.lazyInit().Values }
+func (ed *Enum) ReservedNames() pref.Names { return &ed.lazyInit().ReservedNames }
+func (ed *Enum) ReservedRanges() pref.EnumRanges { return &ed.lazyInit().ReservedRanges }
+func (ed *Enum) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, ed) }
+func (ed *Enum) ProtoType(pref.EnumDescriptor) {}
+func (ed *Enum) lazyInit() *EnumL2 {
+ ed.L0.ParentFile.lazyInit() // implicitly initializes L2
+ return ed.L2
+}
+
+func (ed *EnumValue) Options() pref.ProtoMessage {
+ if f := ed.L1.Options; f != nil {
+ return f()
+ }
+ return descopts.EnumValue
+}
+func (ed *EnumValue) Number() pref.EnumNumber { return ed.L1.Number }
+func (ed *EnumValue) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, ed) }
+func (ed *EnumValue) ProtoType(pref.EnumValueDescriptor) {}
+
+type (
+ Message struct {
+ Base
+ L1 MessageL1
+ L2 *MessageL2 // protected by fileDesc.once
+ }
+ MessageL1 struct {
+ Enums Enums
+ Messages Messages
+ Extensions Extensions
+ }
+ MessageL2 struct {
+ Options func() pref.ProtoMessage
+ IsMapEntry bool // promoted from google.protobuf.MessageOptions
+ IsMessageSet bool // promoted from google.protobuf.MessageOptions
+ Fields Fields
+ Oneofs Oneofs
+ ReservedNames Names
+ ReservedRanges FieldRanges
+ RequiredNumbers FieldNumbers // must be consistent with Fields.Cardinality
+ ExtensionRanges FieldRanges
+ ExtensionRangeOptions []func() pref.ProtoMessage // must be same length as ExtensionRanges
+ }
+
+ Field struct {
+ Base
+ L1 FieldL1
+ }
+ FieldL1 struct {
+ Options func() pref.ProtoMessage
+ Number pref.FieldNumber
+ Cardinality pref.Cardinality // must be consistent with Message.RequiredNumbers
+ Kind pref.Kind
+ JSONName jsonName
+ IsWeak bool // promoted from google.protobuf.FieldOptions
+ HasPacked bool // promoted from google.protobuf.FieldOptions
+ IsPacked bool // promoted from google.protobuf.FieldOptions
+ Default defaultValue
+ ContainingOneof pref.OneofDescriptor // must be consistent with Message.Oneofs.Fields
+ Enum pref.EnumDescriptor
+ Message pref.MessageDescriptor
+ }
+
+ Oneof struct {
+ Base
+ L1 OneofL1
+ }
+ OneofL1 struct {
+ Options func() pref.ProtoMessage
+ Fields OneofFields // must be consistent with Message.Fields.ContainingOneof
+ }
+)
+
+func (md *Message) Options() pref.ProtoMessage {
+ if f := md.lazyInit().Options; f != nil {
+ return f()
+ }
+ return descopts.Message
+}
+func (md *Message) IsMapEntry() bool { return md.lazyInit().IsMapEntry }
+func (md *Message) Fields() pref.FieldDescriptors { return &md.lazyInit().Fields }
+func (md *Message) Oneofs() pref.OneofDescriptors { return &md.lazyInit().Oneofs }
+func (md *Message) ReservedNames() pref.Names { return &md.lazyInit().ReservedNames }
+func (md *Message) ReservedRanges() pref.FieldRanges { return &md.lazyInit().ReservedRanges }
+func (md *Message) RequiredNumbers() pref.FieldNumbers { return &md.lazyInit().RequiredNumbers }
+func (md *Message) ExtensionRanges() pref.FieldRanges { return &md.lazyInit().ExtensionRanges }
+func (md *Message) ExtensionRangeOptions(i int) pref.ProtoMessage {
+ if f := md.lazyInit().ExtensionRangeOptions[i]; f != nil {
+ return f()
+ }
+ return descopts.ExtensionRange
+}
+func (md *Message) Enums() pref.EnumDescriptors { return &md.L1.Enums }
+func (md *Message) Messages() pref.MessageDescriptors { return &md.L1.Messages }
+func (md *Message) Extensions() pref.ExtensionDescriptors { return &md.L1.Extensions }
+func (md *Message) ProtoType(pref.MessageDescriptor) {}
+func (md *Message) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, md) }
+func (md *Message) lazyInit() *MessageL2 {
+ md.L0.ParentFile.lazyInit() // implicitly initializes L2
+ return md.L2
+}
+
+// IsMessageSet is a pseudo-internal API for checking whether a message
+// should serialize in the proto1 message format.
+//
+// WARNING: This method is exempt from the compatibility promise and may be
+// removed in the future without warning.
+func (md *Message) IsMessageSet() bool {
+ return md.lazyInit().IsMessageSet
+}
+
+func (fd *Field) Options() pref.ProtoMessage {
+ if f := fd.L1.Options; f != nil {
+ return f()
+ }
+ return descopts.Field
+}
+func (fd *Field) Number() pref.FieldNumber { return fd.L1.Number }
+func (fd *Field) Cardinality() pref.Cardinality { return fd.L1.Cardinality }
+func (fd *Field) Kind() pref.Kind { return fd.L1.Kind }
+func (fd *Field) HasJSONName() bool { return fd.L1.JSONName.has }
+func (fd *Field) JSONName() string { return fd.L1.JSONName.get(fd) }
+func (fd *Field) IsPacked() bool {
+ if !fd.L1.HasPacked && fd.L0.ParentFile.L1.Syntax != pref.Proto2 && fd.L1.Cardinality == pref.Repeated {
+ switch fd.L1.Kind {
+ case pref.StringKind, pref.BytesKind, pref.MessageKind, pref.GroupKind:
+ default:
+ return true
+ }
+ }
+ return fd.L1.IsPacked
+}
+func (fd *Field) IsExtension() bool { return false }
+func (fd *Field) IsWeak() bool { return fd.L1.IsWeak }
+func (fd *Field) IsList() bool { return fd.Cardinality() == pref.Repeated && !fd.IsMap() }
+func (fd *Field) IsMap() bool { return fd.Message() != nil && fd.Message().IsMapEntry() }
+func (fd *Field) MapKey() pref.FieldDescriptor {
+ if !fd.IsMap() {
+ return nil
+ }
+ return fd.Message().Fields().ByNumber(1)
+}
+func (fd *Field) MapValue() pref.FieldDescriptor {
+ if !fd.IsMap() {
+ return nil
+ }
+ return fd.Message().Fields().ByNumber(2)
+}
+func (fd *Field) HasDefault() bool { return fd.L1.Default.has }
+func (fd *Field) Default() pref.Value { return fd.L1.Default.get(fd) }
+func (fd *Field) DefaultEnumValue() pref.EnumValueDescriptor { return fd.L1.Default.enum }
+func (fd *Field) ContainingOneof() pref.OneofDescriptor { return fd.L1.ContainingOneof }
+func (fd *Field) ContainingMessage() pref.MessageDescriptor {
+ return fd.L0.Parent.(pref.MessageDescriptor)
+}
+func (fd *Field) Enum() pref.EnumDescriptor { return fd.L1.Enum }
+func (fd *Field) Message() pref.MessageDescriptor { return fd.L1.Message }
+func (fd *Field) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, fd) }
+func (fd *Field) ProtoType(pref.FieldDescriptor) {}
+
+func (od *Oneof) Options() pref.ProtoMessage {
+ if f := od.L1.Options; f != nil {
+ return f()
+ }
+ return descopts.Oneof
+}
+func (od *Oneof) Fields() pref.FieldDescriptors { return &od.L1.Fields }
+func (od *Oneof) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, od) }
+func (od *Oneof) ProtoType(pref.OneofDescriptor) {}
+
+type (
+ Extension struct {
+ Base
+ L1 ExtensionL1
+ L2 *ExtensionL2 // protected by fileDesc.once
+ }
+ ExtensionL1 struct {
+ Number pref.FieldNumber
+ Extendee pref.MessageDescriptor
+ Kind pref.Kind
+ }
+ ExtensionL2 struct {
+ Options func() pref.ProtoMessage
+ Cardinality pref.Cardinality
+ JSONName jsonName
+ IsPacked bool // promoted from google.protobuf.FieldOptions
+ Default defaultValue
+ Enum pref.EnumDescriptor
+ Message pref.MessageDescriptor
+ }
+)
+
+func (xd *Extension) Options() pref.ProtoMessage {
+ if f := xd.lazyInit().Options; f != nil {
+ return f()
+ }
+ return descopts.Field
+}
+func (xd *Extension) Number() pref.FieldNumber { return xd.L1.Number }
+func (xd *Extension) Cardinality() pref.Cardinality { return xd.lazyInit().Cardinality }
+func (xd *Extension) Kind() pref.Kind { return xd.L1.Kind }
+func (xd *Extension) HasJSONName() bool { return xd.lazyInit().JSONName.has }
+func (xd *Extension) JSONName() string { return xd.lazyInit().JSONName.get(xd) }
+func (xd *Extension) IsPacked() bool { return xd.lazyInit().IsPacked }
+func (xd *Extension) IsExtension() bool { return true }
+func (xd *Extension) IsWeak() bool { return false }
+func (xd *Extension) IsList() bool { return xd.Cardinality() == pref.Repeated }
+func (xd *Extension) IsMap() bool { return false }
+func (xd *Extension) MapKey() pref.FieldDescriptor { return nil }
+func (xd *Extension) MapValue() pref.FieldDescriptor { return nil }
+func (xd *Extension) HasDefault() bool { return xd.lazyInit().Default.has }
+func (xd *Extension) Default() pref.Value { return xd.lazyInit().Default.get(xd) }
+func (xd *Extension) DefaultEnumValue() pref.EnumValueDescriptor { return xd.lazyInit().Default.enum }
+func (xd *Extension) ContainingOneof() pref.OneofDescriptor { return nil }
+func (xd *Extension) ContainingMessage() pref.MessageDescriptor { return xd.L1.Extendee }
+func (xd *Extension) Enum() pref.EnumDescriptor { return xd.lazyInit().Enum }
+func (xd *Extension) Message() pref.MessageDescriptor { return xd.lazyInit().Message }
+func (xd *Extension) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, xd) }
+func (xd *Extension) ProtoType(pref.FieldDescriptor) {}
+func (xd *Extension) ProtoInternal(pragma.DoNotImplement) {}
+func (xd *Extension) lazyInit() *ExtensionL2 {
+ xd.L0.ParentFile.lazyInit() // implicitly initializes L2
+ return xd.L2
+}
+
+type (
+ Service struct {
+ Base
+ L1 ServiceL1
+ L2 *ServiceL2 // protected by fileDesc.once
+ }
+ ServiceL1 struct{}
+ ServiceL2 struct {
+ Options func() pref.ProtoMessage
+ Methods Methods
+ }
+
+ Method struct {
+ Base
+ L1 MethodL1
+ }
+ MethodL1 struct {
+ Options func() pref.ProtoMessage
+ Input pref.MessageDescriptor
+ Output pref.MessageDescriptor
+ IsStreamingClient bool
+ IsStreamingServer bool
+ }
+)
+
+func (sd *Service) Options() pref.ProtoMessage {
+ if f := sd.lazyInit().Options; f != nil {
+ return f()
+ }
+ return descopts.Service
+}
+func (sd *Service) Methods() pref.MethodDescriptors { return &sd.lazyInit().Methods }
+func (sd *Service) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, sd) }
+func (sd *Service) ProtoType(pref.ServiceDescriptor) {}
+func (sd *Service) ProtoInternal(pragma.DoNotImplement) {}
+func (sd *Service) lazyInit() *ServiceL2 {
+ sd.L0.ParentFile.lazyInit() // implicitly initializes L2
+ return sd.L2
+}
+
+func (md *Method) Options() pref.ProtoMessage {
+ if f := md.L1.Options; f != nil {
+ return f()
+ }
+ return descopts.Method
+}
+func (md *Method) Input() pref.MessageDescriptor { return md.L1.Input }
+func (md *Method) Output() pref.MessageDescriptor { return md.L1.Output }
+func (md *Method) IsStreamingClient() bool { return md.L1.IsStreamingClient }
+func (md *Method) IsStreamingServer() bool { return md.L1.IsStreamingServer }
+func (md *Method) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, md) }
+func (md *Method) ProtoType(pref.MethodDescriptor) {}
+func (md *Method) ProtoInternal(pragma.DoNotImplement) {}
+
+// Surrogate files are can be used to create standalone descriptors
+// where the syntax is only information derived from the parent file.
+var (
+ SurrogateProto2 = &File{L1: FileL1{Syntax: pref.Proto2}, L2: &FileL2{}}
+ SurrogateProto3 = &File{L1: FileL1{Syntax: pref.Proto3}, L2: &FileL2{}}
+)
+
+type (
+ Base struct {
+ L0 BaseL0
+ }
+ BaseL0 struct {
+ FullName pref.FullName // must be populated
+ ParentFile *File // must be populated
+ Parent pref.Descriptor
+ Index int
+ }
+)
+
+func (d *Base) Name() pref.Name { return d.L0.FullName.Name() }
+func (d *Base) FullName() pref.FullName { return d.L0.FullName }
+func (d *Base) ParentFile() pref.FileDescriptor {
+ if d.L0.ParentFile == SurrogateProto2 || d.L0.ParentFile == SurrogateProto3 {
+ return nil // surrogate files are not real parents
+ }
+ return d.L0.ParentFile
+}
+func (d *Base) Parent() pref.Descriptor { return d.L0.Parent }
+func (d *Base) Index() int { return d.L0.Index }
+func (d *Base) Syntax() pref.Syntax { return d.L0.ParentFile.Syntax() }
+func (d *Base) IsPlaceholder() bool { return false }
+func (d *Base) ProtoInternal(pragma.DoNotImplement) {}
+
+func JSONName(s string) jsonName {
+ return jsonName{has: true, name: s}
+}
+
+type jsonName struct {
+ has bool
+ once sync.Once
+ name string
+}
+
+func (js *jsonName) get(fd pref.FieldDescriptor) string {
+ if !js.has {
+ js.once.Do(func() {
+ js.name = makeJSONName(fd.Name())
+ })
+ }
+ return js.name
+}
+
+// makeJSONName creates a JSON name from the protobuf short name.
+func makeJSONName(s pref.Name) string {
+ var b []byte
+ var wasUnderscore bool
+ for i := 0; i < len(s); i++ { // proto identifiers are always ASCII
+ c := s[i]
+ if c != '_' {
+ isLower := 'a' <= c && c <= 'z'
+ if wasUnderscore && isLower {
+ c -= 'a' - 'A'
+ }
+ b = append(b, c)
+ }
+ wasUnderscore = c == '_'
+ }
+ return string(b)
+}
+
+func DefaultValue(v pref.Value, ev pref.EnumValueDescriptor) defaultValue {
+ dv := defaultValue{has: v.IsValid(), val: v, enum: ev}
+ if b, ok := v.Interface().([]byte); ok {
+ // Store a copy of the default bytes, so that we can detect
+ // accidental mutations of the original value.
+ dv.bytes = append([]byte(nil), b...)
+ }
+ return dv
+}
+
+func unmarshalDefault(b []byte, k pref.Kind, pf *File, ed pref.EnumDescriptor) defaultValue {
+ var evs pref.EnumValueDescriptors
+ if k == pref.EnumKind {
+ // If the enum is declared within the same file, be careful not to
+ // blindly call the Values method, lest we bind ourselves in a deadlock.
+ if ed, ok := ed.(*Enum); ok && ed.L0.ParentFile == pf {
+ evs = &ed.L2.Values
+ } else {
+ evs = ed.Values()
+ }
+ }
+
+ v, ev, err := defval.Unmarshal(string(b), k, evs, defval.Descriptor)
+ if err != nil {
+ panic(err)
+ }
+ return DefaultValue(v, ev)
+}
+
+type defaultValue struct {
+ has bool
+ val pref.Value
+ enum pref.EnumValueDescriptor
+ bytes []byte
+}
+
+func (dv *defaultValue) get(fd pref.FieldDescriptor) pref.Value {
+ // Return the zero value as the default if unpopulated.
+ if !dv.has {
+ switch fd.Kind() {
+ case pref.BoolKind:
+ return pref.ValueOf(false)
+ case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
+ return pref.ValueOf(int32(0))
+ case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
+ return pref.ValueOf(int64(0))
+ case pref.Uint32Kind, pref.Fixed32Kind:
+ return pref.ValueOf(uint32(0))
+ case pref.Uint64Kind, pref.Fixed64Kind:
+ return pref.ValueOf(uint64(0))
+ case pref.FloatKind:
+ return pref.ValueOf(float32(0))
+ case pref.DoubleKind:
+ return pref.ValueOf(float64(0))
+ case pref.StringKind:
+ return pref.ValueOf(string(""))
+ case pref.BytesKind:
+ return pref.ValueOf([]byte(nil))
+ case pref.EnumKind:
+ return pref.ValueOf(fd.Enum().Values().Get(0).Number())
+ }
+ }
+
+ if len(dv.bytes) > 0 && !bytes.Equal(dv.bytes, dv.val.Bytes()) {
+ // TODO: Avoid panic if we're running with the race detector
+ // and instead spawn a goroutine that periodically resets
+ // this value back to the original to induce a race.
+ panic("detected mutation on the default bytes")
+ }
+ return dv.val
+}
diff --git a/internal/filedesc/desc_init.go b/internal/filedesc/desc_init.go
new file mode 100644
index 0000000..03abf8f
--- /dev/null
+++ b/internal/filedesc/desc_init.go
@@ -0,0 +1,390 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package filedesc
+
+import (
+ "google.golang.org/protobuf/internal/encoding/wire"
+ "google.golang.org/protobuf/internal/fieldnum"
+ pref "google.golang.org/protobuf/reflect/protoreflect"
+)
+
+// fileRaw is a data struct used when initializing a file descriptor from
+// a raw FileDescriptorProto.
+type fileRaw struct {
+ builder DescBuilder
+ allEnums []Enum
+ allMessages []Message
+ allExtensions []Extension
+ allServices []Service
+}
+
+func newRawFile(db DescBuilder) *File {
+ fd := &File{fileRaw: fileRaw{builder: db}}
+ fd.initDecls(db.NumEnums, db.NumMessages, db.NumExtensions, db.NumServices)
+ fd.unmarshalSeed(db.RawDescriptor)
+
+ // Extended message targets are eagerly resolved since registration
+ // needs this information at program init time.
+ for i := range fd.allExtensions {
+ xd := &fd.allExtensions[i]
+ xd.L1.Extendee = fd.resolveMessageDependency(xd.L1.Extendee, listExtTargets, int32(i))
+ }
+
+ fd.checkDecls()
+ return fd
+}
+
+// initDecls pre-allocates slices for the exact number of enums, messages
+// (including map entries), extensions, and services declared in the proto file.
+// This is done to avoid regrowing the slice, which would change the address
+// for any previously seen declaration.
+//
+// The alloc methods "allocates" slices by pulling from the capacity.
+func (fd *File) initDecls(numEnums, numMessages, numExtensions, numServices int32) {
+ fd.allEnums = make([]Enum, 0, numEnums)
+ fd.allMessages = make([]Message, 0, numMessages)
+ fd.allExtensions = make([]Extension, 0, numExtensions)
+ fd.allServices = make([]Service, 0, numServices)
+}
+
+func (fd *File) allocEnums(n int) []Enum {
+ total := len(fd.allEnums)
+ es := fd.allEnums[total : total+n]
+ fd.allEnums = fd.allEnums[:total+n]
+ return es
+}
+func (fd *File) allocMessages(n int) []Message {
+ total := len(fd.allMessages)
+ ms := fd.allMessages[total : total+n]
+ fd.allMessages = fd.allMessages[:total+n]
+ return ms
+}
+func (fd *File) allocExtensions(n int) []Extension {
+ total := len(fd.allExtensions)
+ xs := fd.allExtensions[total : total+n]
+ fd.allExtensions = fd.allExtensions[:total+n]
+ return xs
+}
+func (fd *File) allocServices(n int) []Service {
+ total := len(fd.allServices)
+ xs := fd.allServices[total : total+n]
+ fd.allServices = fd.allServices[:total+n]
+ return xs
+}
+
+// checkDecls performs a sanity check that the expected number of expected
+// declarations matches the number that were found in the descriptor proto.
+func (fd *File) checkDecls() {
+ switch {
+ case len(fd.allEnums) != cap(fd.allEnums):
+ case len(fd.allMessages) != cap(fd.allMessages):
+ case len(fd.allExtensions) != cap(fd.allExtensions):
+ case len(fd.allServices) != cap(fd.allServices):
+ default:
+ return
+ }
+ panic("mismatching cardinality")
+}
+
+func (fd *File) unmarshalSeed(b []byte) {
+ nb := getNameBuilder()
+ defer putNameBuilder(nb)
+
+ var prevField pref.FieldNumber
+ var numEnums, numMessages, numExtensions, numServices int
+ var posEnums, posMessages, posExtensions, posServices int
+ b0 := b
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.BytesType:
+ v, m := wire.ConsumeBytes(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.FileDescriptorProto_Syntax:
+ switch string(v) {
+ case "proto2":
+ fd.L1.Syntax = pref.Proto2
+ case "proto3":
+ fd.L1.Syntax = pref.Proto3
+ default:
+ panic("invalid syntax")
+ }
+ case fieldnum.FileDescriptorProto_Name:
+ fd.L1.Path = nb.MakeString(v)
+ case fieldnum.FileDescriptorProto_Package:
+ fd.L1.Package = pref.FullName(nb.MakeString(v))
+ case fieldnum.FileDescriptorProto_EnumType:
+ if prevField != fieldnum.FileDescriptorProto_EnumType {
+ if numEnums > 0 {
+ panic("non-contiguous repeated field")
+ }
+ posEnums = len(b0) - len(b) - n - m
+ }
+ numEnums++
+ case fieldnum.FileDescriptorProto_MessageType:
+ if prevField != fieldnum.FileDescriptorProto_MessageType {
+ if numMessages > 0 {
+ panic("non-contiguous repeated field")
+ }
+ posMessages = len(b0) - len(b) - n - m
+ }
+ numMessages++
+ case fieldnum.FileDescriptorProto_Extension:
+ if prevField != fieldnum.FileDescriptorProto_Extension {
+ if numExtensions > 0 {
+ panic("non-contiguous repeated field")
+ }
+ posExtensions = len(b0) - len(b) - n - m
+ }
+ numExtensions++
+ case fieldnum.FileDescriptorProto_Service:
+ if prevField != fieldnum.FileDescriptorProto_Service {
+ if numServices > 0 {
+ panic("non-contiguous repeated field")
+ }
+ posServices = len(b0) - len(b) - n - m
+ }
+ numServices++
+ }
+ prevField = num
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ prevField = -1 // ignore known field numbers of unknown wire type
+ }
+ }
+
+ // If syntax is missing, it is assumed to be proto2.
+ if fd.L1.Syntax == 0 {
+ fd.L1.Syntax = pref.Proto2
+ }
+
+ // Must allocate all declarations before parsing each descriptor type
+ // to ensure we handled all descriptors in "flattened ordering".
+ if numEnums > 0 {
+ fd.L1.Enums.List = fd.allocEnums(numEnums)
+ }
+ if numMessages > 0 {
+ fd.L1.Messages.List = fd.allocMessages(numMessages)
+ }
+ if numExtensions > 0 {
+ fd.L1.Extensions.List = fd.allocExtensions(numExtensions)
+ }
+ if numServices > 0 {
+ fd.L1.Services.List = fd.allocServices(numServices)
+ }
+
+ if numEnums > 0 {
+ b := b0[posEnums:]
+ for i := range fd.L1.Enums.List {
+ _, n := wire.ConsumeVarint(b)
+ v, m := wire.ConsumeBytes(b[n:])
+ fd.L1.Enums.List[i].unmarshalSeed(v, nb, fd, fd, i)
+ b = b[n+m:]
+ }
+ }
+ if numMessages > 0 {
+ b := b0[posMessages:]
+ for i := range fd.L1.Messages.List {
+ _, n := wire.ConsumeVarint(b)
+ v, m := wire.ConsumeBytes(b[n:])
+ fd.L1.Messages.List[i].unmarshalSeed(v, nb, fd, fd, i)
+ b = b[n+m:]
+ }
+ }
+ if numExtensions > 0 {
+ b := b0[posExtensions:]
+ for i := range fd.L1.Extensions.List {
+ _, n := wire.ConsumeVarint(b)
+ v, m := wire.ConsumeBytes(b[n:])
+ fd.L1.Extensions.List[i].unmarshalSeed(v, nb, fd, fd, i)
+ b = b[n+m:]
+ }
+ }
+ if numServices > 0 {
+ b := b0[posServices:]
+ for i := range fd.L1.Services.List {
+ _, n := wire.ConsumeVarint(b)
+ v, m := wire.ConsumeBytes(b[n:])
+ fd.L1.Services.List[i].unmarshalSeed(v, nb, fd, fd, i)
+ b = b[n+m:]
+ }
+ }
+}
+
+func (ed *Enum) unmarshalSeed(b []byte, nb *nameBuilder, pf *File, pd pref.Descriptor, i int) {
+ ed.L0.ParentFile = pf
+ ed.L0.Parent = pd
+ ed.L0.Index = i
+
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.BytesType:
+ v, m := wire.ConsumeBytes(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.EnumDescriptorProto_Name:
+ ed.L0.FullName = nb.AppendFullName(pd.FullName(), v)
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+}
+
+func (md *Message) unmarshalSeed(b []byte, nb *nameBuilder, pf *File, pd pref.Descriptor, i int) {
+ md.L0.ParentFile = pf
+ md.L0.Parent = pd
+ md.L0.Index = i
+
+ var prevField pref.FieldNumber
+ var numEnums, numMessages, numExtensions int
+ var posEnums, posMessages, posExtensions int
+ b0 := b
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.BytesType:
+ v, m := wire.ConsumeBytes(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.DescriptorProto_Name:
+ md.L0.FullName = nb.AppendFullName(pd.FullName(), v)
+ case fieldnum.DescriptorProto_EnumType:
+ if prevField != fieldnum.DescriptorProto_EnumType {
+ if numEnums > 0 {
+ panic("non-contiguous repeated field")
+ }
+ posEnums = len(b0) - len(b) - n - m
+ }
+ numEnums++
+ case fieldnum.DescriptorProto_NestedType:
+ if prevField != fieldnum.DescriptorProto_NestedType {
+ if numMessages > 0 {
+ panic("non-contiguous repeated field")
+ }
+ posMessages = len(b0) - len(b) - n - m
+ }
+ numMessages++
+ case fieldnum.DescriptorProto_Extension:
+ if prevField != fieldnum.DescriptorProto_Extension {
+ if numExtensions > 0 {
+ panic("non-contiguous repeated field")
+ }
+ posExtensions = len(b0) - len(b) - n - m
+ }
+ numExtensions++
+ }
+ prevField = num
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ prevField = -1 // ignore known field numbers of unknown wire type
+ }
+ }
+
+ // Must allocate all declarations before parsing each descriptor type
+ // to ensure we handled all descriptors in "flattened ordering".
+ if numEnums > 0 {
+ md.L1.Enums.List = pf.allocEnums(numEnums)
+ }
+ if numMessages > 0 {
+ md.L1.Messages.List = pf.allocMessages(numMessages)
+ }
+ if numExtensions > 0 {
+ md.L1.Extensions.List = pf.allocExtensions(numExtensions)
+ }
+
+ if numEnums > 0 {
+ b := b0[posEnums:]
+ for i := range md.L1.Enums.List {
+ _, n := wire.ConsumeVarint(b)
+ v, m := wire.ConsumeBytes(b[n:])
+ md.L1.Enums.List[i].unmarshalSeed(v, nb, pf, md, i)
+ b = b[n+m:]
+ }
+ }
+ if numMessages > 0 {
+ b := b0[posMessages:]
+ for i := range md.L1.Messages.List {
+ _, n := wire.ConsumeVarint(b)
+ v, m := wire.ConsumeBytes(b[n:])
+ md.L1.Messages.List[i].unmarshalSeed(v, nb, pf, md, i)
+ b = b[n+m:]
+ }
+ }
+ if numExtensions > 0 {
+ b := b0[posExtensions:]
+ for i := range md.L1.Extensions.List {
+ _, n := wire.ConsumeVarint(b)
+ v, m := wire.ConsumeBytes(b[n:])
+ md.L1.Extensions.List[i].unmarshalSeed(v, nb, pf, md, i)
+ b = b[n+m:]
+ }
+ }
+}
+
+func (xd *Extension) unmarshalSeed(b []byte, nb *nameBuilder, pf *File, pd pref.Descriptor, i int) {
+ xd.L0.ParentFile = pf
+ xd.L0.Parent = pd
+ xd.L0.Index = i
+
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.VarintType:
+ v, m := wire.ConsumeVarint(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.FieldDescriptorProto_Number:
+ xd.L1.Number = pref.FieldNumber(v)
+ case fieldnum.FieldDescriptorProto_Type:
+ xd.L1.Kind = pref.Kind(v)
+ }
+ case wire.BytesType:
+ v, m := wire.ConsumeBytes(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.FieldDescriptorProto_Name:
+ xd.L0.FullName = nb.AppendFullName(pd.FullName(), v)
+ case fieldnum.FieldDescriptorProto_Extendee:
+ xd.L1.Extendee = PlaceholderMessage(nb.MakeFullName(v))
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+}
+
+func (sd *Service) unmarshalSeed(b []byte, nb *nameBuilder, pf *File, pd pref.Descriptor, i int) {
+ sd.L0.ParentFile = pf
+ sd.L0.Parent = pd
+ sd.L0.Index = i
+
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.BytesType:
+ v, m := wire.ConsumeBytes(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.ServiceDescriptorProto_Name:
+ sd.L0.FullName = nb.AppendFullName(pd.FullName(), v)
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+}
diff --git a/internal/filedesc/desc_lazy.go b/internal/filedesc/desc_lazy.go
new file mode 100644
index 0000000..8b8ab5a
--- /dev/null
+++ b/internal/filedesc/desc_lazy.go
@@ -0,0 +1,686 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package filedesc
+
+import (
+ "reflect"
+
+ "google.golang.org/protobuf/internal/descopts"
+ "google.golang.org/protobuf/internal/encoding/wire"
+ "google.golang.org/protobuf/internal/fieldnum"
+ "google.golang.org/protobuf/proto"
+ pref "google.golang.org/protobuf/reflect/protoreflect"
+)
+
+func (fd *File) lazyRawInit() {
+ fd.unmarshalFull(fd.builder.RawDescriptor)
+ fd.resolveMessages()
+ fd.resolveExtensions()
+ fd.resolveServices()
+}
+
+func (file *File) resolveMessages() {
+ var depIdx int32
+ for i := range file.allMessages {
+ md := &file.allMessages[i]
+
+ // Resolve message field dependencies.
+ for j := range md.L2.Fields.List {
+ fd := &md.L2.Fields.List[j]
+
+ // Weak fields are only resolved by name.
+ if fd.L1.IsWeak {
+ r := file.builder.FileRegistry
+ if md, _ := r.FindMessageByName(fd.L1.Message.FullName()); md != nil {
+ fd.L1.Message = md
+ }
+ continue
+ }
+
+ // Resolve message field dependency.
+ switch fd.L1.Kind {
+ case pref.EnumKind:
+ fd.L1.Enum = file.resolveEnumDependency(fd.L1.Enum, listFieldDeps, depIdx)
+ depIdx++
+ case pref.MessageKind, pref.GroupKind:
+ fd.L1.Message = file.resolveMessageDependency(fd.L1.Message, listFieldDeps, depIdx)
+ depIdx++
+ }
+
+ // Default is resolved here since it depends on Enum being resolved.
+ if v := fd.L1.Default.val; v.IsValid() {
+ fd.L1.Default = unmarshalDefault(v.Bytes(), fd.L1.Kind, file, fd.L1.Enum)
+ }
+ }
+ }
+}
+
+func (file *File) resolveExtensions() {
+ var depIdx int32
+ for i := range file.allExtensions {
+ xd := &file.allExtensions[i]
+
+ // Resolve extension field dependency.
+ switch xd.L1.Kind {
+ case pref.EnumKind:
+ xd.L2.Enum = file.resolveEnumDependency(xd.L2.Enum, listExtDeps, depIdx)
+ depIdx++
+ case pref.MessageKind, pref.GroupKind:
+ xd.L2.Message = file.resolveMessageDependency(xd.L2.Message, listExtDeps, depIdx)
+ depIdx++
+ }
+
+ // Default is resolved here since it depends on Enum being resolved.
+ if v := xd.L2.Default.val; v.IsValid() {
+ xd.L2.Default = unmarshalDefault(v.Bytes(), xd.L1.Kind, file, xd.L2.Enum)
+ }
+ }
+}
+
+func (file *File) resolveServices() {
+ var depIdx int32
+ for i := range file.allServices {
+ sd := &file.allServices[i]
+
+ // Resolve method dependencies.
+ for j := range sd.L2.Methods.List {
+ md := &sd.L2.Methods.List[j]
+ md.L1.Input = file.resolveMessageDependency(md.L1.Input, listMethInDeps, depIdx)
+ md.L1.Output = file.resolveMessageDependency(md.L1.Output, listMethOutDeps, depIdx)
+ depIdx++
+ }
+ }
+}
+
+func (file *File) resolveEnumDependency(ed pref.EnumDescriptor, i, j int32) pref.EnumDescriptor {
+ r := file.builder.FileRegistry
+ if r, ok := r.(resolverByIndex); ok {
+ if ed2 := r.FindEnumByIndex(i, j, file.allEnums, file.allMessages); ed2 != nil {
+ return ed2
+ }
+ }
+ for i := range file.allEnums {
+ if ed2 := &file.allEnums[i]; ed2.L0.FullName == ed.FullName() {
+ return ed2
+ }
+ }
+ if ed2, _ := r.FindEnumByName(ed.FullName()); ed2 != nil {
+ return ed2
+ }
+ return ed
+}
+
+func (file *File) resolveMessageDependency(md pref.MessageDescriptor, i, j int32) pref.MessageDescriptor {
+ r := file.builder.FileRegistry
+ if r, ok := r.(resolverByIndex); ok {
+ if md2 := r.FindMessageByIndex(i, j, file.allEnums, file.allMessages); md2 != nil {
+ return md2
+ }
+ }
+ for i := range file.allMessages {
+ if md2 := &file.allMessages[i]; md2.L0.FullName == md.FullName() {
+ return md2
+ }
+ }
+ if md2, _ := r.FindMessageByName(md.FullName()); md2 != nil {
+ return md2
+ }
+ return md
+}
+
+func (fd *File) unmarshalFull(b []byte) {
+ nb := getNameBuilder()
+ defer putNameBuilder(nb)
+
+ var enumIdx, messageIdx, extensionIdx, serviceIdx int
+ var rawOptions []byte
+ fd.L2 = new(FileL2)
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.VarintType:
+ v, m := wire.ConsumeVarint(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.FileDescriptorProto_PublicDependency:
+ fd.L2.Imports[v].IsPublic = true
+ case fieldnum.FileDescriptorProto_WeakDependency:
+ fd.L2.Imports[v].IsWeak = true
+ }
+ case wire.BytesType:
+ v, m := wire.ConsumeBytes(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.FileDescriptorProto_Dependency:
+ path := nb.MakeString(v)
+ imp, _ := fd.builder.FileRegistry.FindFileByPath(path)
+ if imp == nil {
+ imp = PlaceholderFile(path)
+ }
+ fd.L2.Imports = append(fd.L2.Imports, pref.FileImport{FileDescriptor: imp})
+ case fieldnum.FileDescriptorProto_EnumType:
+ fd.L1.Enums.List[enumIdx].unmarshalFull(v, nb)
+ enumIdx++
+ case fieldnum.FileDescriptorProto_MessageType:
+ fd.L1.Messages.List[messageIdx].unmarshalFull(v, nb)
+ messageIdx++
+ case fieldnum.FileDescriptorProto_Extension:
+ fd.L1.Extensions.List[extensionIdx].unmarshalFull(v, nb)
+ extensionIdx++
+ case fieldnum.FileDescriptorProto_Service:
+ fd.L1.Services.List[serviceIdx].unmarshalFull(v, nb)
+ serviceIdx++
+ case fieldnum.FileDescriptorProto_Options:
+ rawOptions = appendOptions(rawOptions, v)
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+ fd.L2.Options = fd.builder.optionsUnmarshaler(descopts.File, rawOptions)
+}
+
+func (ed *Enum) unmarshalFull(b []byte, nb *nameBuilder) {
+ var rawValues [][]byte
+ var rawOptions []byte
+ ed.L2 = new(EnumL2)
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.BytesType:
+ v, m := wire.ConsumeBytes(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.EnumDescriptorProto_Value:
+ rawValues = append(rawValues, v)
+ case fieldnum.EnumDescriptorProto_ReservedName:
+ ed.L2.ReservedNames.List = append(ed.L2.ReservedNames.List, pref.Name(nb.MakeString(v)))
+ case fieldnum.EnumDescriptorProto_ReservedRange:
+ ed.L2.ReservedRanges.List = append(ed.L2.ReservedRanges.List, unmarshalEnumReservedRange(v))
+ case fieldnum.EnumDescriptorProto_Options:
+ rawOptions = appendOptions(rawOptions, v)
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+ if len(rawValues) > 0 {
+ ed.L2.Values.List = make([]EnumValue, len(rawValues))
+ for i, b := range rawValues {
+ ed.L2.Values.List[i].unmarshalFull(b, nb, ed.L0.ParentFile, ed, i)
+ }
+ }
+ ed.L2.Options = ed.L0.ParentFile.builder.optionsUnmarshaler(descopts.Enum, rawOptions)
+}
+
+func unmarshalEnumReservedRange(b []byte) (r [2]pref.EnumNumber) {
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.VarintType:
+ v, m := wire.ConsumeVarint(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.EnumDescriptorProto_EnumReservedRange_Start:
+ r[0] = pref.EnumNumber(v)
+ case fieldnum.EnumDescriptorProto_EnumReservedRange_End:
+ r[1] = pref.EnumNumber(v)
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+ return r
+}
+
+func (vd *EnumValue) unmarshalFull(b []byte, nb *nameBuilder, pf *File, pd pref.Descriptor, i int) {
+ vd.L0.ParentFile = pf
+ vd.L0.Parent = pd
+ vd.L0.Index = i
+
+ var rawOptions []byte
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.VarintType:
+ v, m := wire.ConsumeVarint(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.EnumValueDescriptorProto_Number:
+ vd.L1.Number = pref.EnumNumber(v)
+ }
+ case wire.BytesType:
+ v, m := wire.ConsumeBytes(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.EnumValueDescriptorProto_Name:
+ // NOTE: Enum values are in the same scope as the enum parent.
+ vd.L0.FullName = nb.AppendFullName(pd.Parent().FullName(), v)
+ case fieldnum.EnumValueDescriptorProto_Options:
+ rawOptions = appendOptions(rawOptions, v)
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+ vd.L1.Options = pf.builder.optionsUnmarshaler(descopts.EnumValue, rawOptions)
+}
+
+func (md *Message) unmarshalFull(b []byte, nb *nameBuilder) {
+ var rawFields, rawOneofs [][]byte
+ var enumIdx, messageIdx, extensionIdx int
+ var rawOptions []byte
+ md.L2 = new(MessageL2)
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.BytesType:
+ v, m := wire.ConsumeBytes(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.DescriptorProto_Field:
+ rawFields = append(rawFields, v)
+ case fieldnum.DescriptorProto_OneofDecl:
+ rawOneofs = append(rawOneofs, v)
+ case fieldnum.DescriptorProto_ReservedName:
+ md.L2.ReservedNames.List = append(md.L2.ReservedNames.List, pref.Name(nb.MakeString(v)))
+ case fieldnum.DescriptorProto_ReservedRange:
+ md.L2.ReservedRanges.List = append(md.L2.ReservedRanges.List, unmarshalMessageReservedRange(v))
+ case fieldnum.DescriptorProto_ExtensionRange:
+ r, rawOptions := unmarshalMessageExtensionRange(v)
+ opts := md.L0.ParentFile.builder.optionsUnmarshaler(descopts.ExtensionRange, rawOptions)
+ md.L2.ExtensionRanges.List = append(md.L2.ExtensionRanges.List, r)
+ md.L2.ExtensionRangeOptions = append(md.L2.ExtensionRangeOptions, opts)
+ case fieldnum.DescriptorProto_EnumType:
+ md.L1.Enums.List[enumIdx].unmarshalFull(v, nb)
+ enumIdx++
+ case fieldnum.DescriptorProto_NestedType:
+ md.L1.Messages.List[messageIdx].unmarshalFull(v, nb)
+ messageIdx++
+ case fieldnum.DescriptorProto_Extension:
+ md.L1.Extensions.List[extensionIdx].unmarshalFull(v, nb)
+ extensionIdx++
+ case fieldnum.DescriptorProto_Options:
+ md.unmarshalOptions(v)
+ rawOptions = appendOptions(rawOptions, v)
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+ if len(rawFields) > 0 || len(rawOneofs) > 0 {
+ md.L2.Fields.List = make([]Field, len(rawFields))
+ md.L2.Oneofs.List = make([]Oneof, len(rawOneofs))
+ for i, b := range rawFields {
+ fd := &md.L2.Fields.List[i]
+ fd.unmarshalFull(b, nb, md.L0.ParentFile, md, i)
+ if fd.L1.Cardinality == pref.Required {
+ md.L2.RequiredNumbers.List = append(md.L2.RequiredNumbers.List, fd.L1.Number)
+ }
+ }
+ for i, b := range rawOneofs {
+ od := &md.L2.Oneofs.List[i]
+ od.unmarshalFull(b, nb, md.L0.ParentFile, md, i)
+ }
+ }
+ md.L2.Options = md.L0.ParentFile.builder.optionsUnmarshaler(descopts.Message, rawOptions)
+}
+
+func (md *Message) unmarshalOptions(b []byte) {
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.VarintType:
+ v, m := wire.ConsumeVarint(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.MessageOptions_MapEntry:
+ md.L2.IsMapEntry = wire.DecodeBool(v)
+ case fieldnum.MessageOptions_MessageSetWireFormat:
+ md.L2.IsMessageSet = wire.DecodeBool(v)
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+}
+
+func unmarshalMessageReservedRange(b []byte) (r [2]pref.FieldNumber) {
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.VarintType:
+ v, m := wire.ConsumeVarint(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.DescriptorProto_ReservedRange_Start:
+ r[0] = pref.FieldNumber(v)
+ case fieldnum.DescriptorProto_ReservedRange_End:
+ r[1] = pref.FieldNumber(v)
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+ return r
+}
+
+func unmarshalMessageExtensionRange(b []byte) (r [2]pref.FieldNumber, rawOptions []byte) {
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.VarintType:
+ v, m := wire.ConsumeVarint(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.DescriptorProto_ExtensionRange_Start:
+ r[0] = pref.FieldNumber(v)
+ case fieldnum.DescriptorProto_ExtensionRange_End:
+ r[1] = pref.FieldNumber(v)
+ }
+ case wire.BytesType:
+ v, m := wire.ConsumeBytes(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.DescriptorProto_ExtensionRange_Options:
+ rawOptions = appendOptions(rawOptions, v)
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+ return r, rawOptions
+}
+
+func (fd *Field) unmarshalFull(b []byte, nb *nameBuilder, pf *File, pd pref.Descriptor, i int) {
+ fd.L0.ParentFile = pf
+ fd.L0.Parent = pd
+ fd.L0.Index = i
+
+ var rawTypeName []byte
+ var rawOptions []byte
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.VarintType:
+ v, m := wire.ConsumeVarint(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.FieldDescriptorProto_Number:
+ fd.L1.Number = pref.FieldNumber(v)
+ case fieldnum.FieldDescriptorProto_Label:
+ fd.L1.Cardinality = pref.Cardinality(v)
+ case fieldnum.FieldDescriptorProto_Type:
+ fd.L1.Kind = pref.Kind(v)
+ case fieldnum.FieldDescriptorProto_OneofIndex:
+ // In Message.unmarshalFull, we allocate slices for both
+ // the field and oneof descriptors before unmarshaling either
+ // of them. This ensures pointers to slice elements are stable.
+ od := &pd.(*Message).L2.Oneofs.List[v]
+ od.L1.Fields.List = append(od.L1.Fields.List, fd)
+ if fd.L1.ContainingOneof != nil {
+ panic("oneof type already set")
+ }
+ fd.L1.ContainingOneof = od
+ }
+ case wire.BytesType:
+ v, m := wire.ConsumeBytes(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.FieldDescriptorProto_Name:
+ fd.L0.FullName = nb.AppendFullName(pd.FullName(), v)
+ case fieldnum.FieldDescriptorProto_JsonName:
+ fd.L1.JSONName = JSONName(nb.MakeString(v))
+ case fieldnum.FieldDescriptorProto_DefaultValue:
+ fd.L1.Default.val = pref.ValueOf(v) // temporarily store as bytes; later resolved in resolveMessages
+ case fieldnum.FieldDescriptorProto_TypeName:
+ rawTypeName = v
+ case fieldnum.FieldDescriptorProto_Options:
+ fd.unmarshalOptions(v)
+ rawOptions = appendOptions(rawOptions, v)
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+ if rawTypeName != nil {
+ name := nb.MakeFullName(rawTypeName)
+ switch fd.L1.Kind {
+ case pref.EnumKind:
+ fd.L1.Enum = PlaceholderEnum(name)
+ case pref.MessageKind, pref.GroupKind:
+ fd.L1.Message = PlaceholderMessage(name)
+ }
+ }
+ fd.L1.Options = pf.builder.optionsUnmarshaler(descopts.Field, rawOptions)
+}
+
+func (fd *Field) unmarshalOptions(b []byte) {
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.VarintType:
+ v, m := wire.ConsumeVarint(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.FieldOptions_Packed:
+ fd.L1.HasPacked = true
+ fd.L1.IsPacked = wire.DecodeBool(v)
+ case fieldnum.FieldOptions_Weak:
+ fd.L1.IsWeak = wire.DecodeBool(v)
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+}
+
+func (od *Oneof) unmarshalFull(b []byte, nb *nameBuilder, pf *File, pd pref.Descriptor, i int) {
+ od.L0.ParentFile = pf
+ od.L0.Parent = pd
+ od.L0.Index = i
+
+ var rawOptions []byte
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.BytesType:
+ v, m := wire.ConsumeBytes(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.OneofDescriptorProto_Name:
+ od.L0.FullName = nb.AppendFullName(pd.FullName(), v)
+ case fieldnum.OneofDescriptorProto_Options:
+ rawOptions = appendOptions(rawOptions, v)
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+ od.L1.Options = pf.builder.optionsUnmarshaler(descopts.Oneof, rawOptions)
+}
+
+func (xd *Extension) unmarshalFull(b []byte, nb *nameBuilder) {
+ var rawTypeName []byte
+ var rawOptions []byte
+ xd.L2 = new(ExtensionL2)
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.VarintType:
+ v, m := wire.ConsumeVarint(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.FieldDescriptorProto_Label:
+ xd.L2.Cardinality = pref.Cardinality(v)
+ }
+ case wire.BytesType:
+ v, m := wire.ConsumeBytes(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.FieldDescriptorProto_JsonName:
+ xd.L2.JSONName = JSONName(nb.MakeString(v))
+ case fieldnum.FieldDescriptorProto_DefaultValue:
+ xd.L2.Default.val = pref.ValueOf(v) // temporarily store as bytes; later resolved in resolveExtensions
+ case fieldnum.FieldDescriptorProto_TypeName:
+ rawTypeName = v
+ case fieldnum.FieldDescriptorProto_Options:
+ xd.unmarshalOptions(v)
+ rawOptions = appendOptions(rawOptions, v)
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+ if rawTypeName != nil {
+ name := nb.MakeFullName(rawTypeName)
+ switch xd.L1.Kind {
+ case pref.EnumKind:
+ xd.L2.Enum = PlaceholderEnum(name)
+ case pref.MessageKind, pref.GroupKind:
+ xd.L2.Message = PlaceholderMessage(name)
+ }
+ }
+ xd.L2.Options = xd.L0.ParentFile.builder.optionsUnmarshaler(descopts.Field, rawOptions)
+}
+
+func (xd *Extension) unmarshalOptions(b []byte) {
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.VarintType:
+ v, m := wire.ConsumeVarint(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.FieldOptions_Packed:
+ xd.L2.IsPacked = wire.DecodeBool(v)
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+}
+
+func (sd *Service) unmarshalFull(b []byte, nb *nameBuilder) {
+ var rawMethods [][]byte
+ var rawOptions []byte
+ sd.L2 = new(ServiceL2)
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.BytesType:
+ v, m := wire.ConsumeBytes(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.ServiceDescriptorProto_Method:
+ rawMethods = append(rawMethods, v)
+ case fieldnum.ServiceDescriptorProto_Options:
+ rawOptions = appendOptions(rawOptions, v)
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+ if len(rawMethods) > 0 {
+ sd.L2.Methods.List = make([]Method, len(rawMethods))
+ for i, b := range rawMethods {
+ sd.L2.Methods.List[i].unmarshalFull(b, nb, sd.L0.ParentFile, sd, i)
+ }
+ }
+ sd.L2.Options = sd.L0.ParentFile.builder.optionsUnmarshaler(descopts.Service, rawOptions)
+}
+
+func (md *Method) unmarshalFull(b []byte, nb *nameBuilder, pf *File, pd pref.Descriptor, i int) {
+ md.L0.ParentFile = pf
+ md.L0.Parent = pd
+ md.L0.Index = i
+
+ var rawOptions []byte
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.VarintType:
+ v, m := wire.ConsumeVarint(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.MethodDescriptorProto_ClientStreaming:
+ md.L1.IsStreamingClient = wire.DecodeBool(v)
+ case fieldnum.MethodDescriptorProto_ServerStreaming:
+ md.L1.IsStreamingServer = wire.DecodeBool(v)
+ }
+ case wire.BytesType:
+ v, m := wire.ConsumeBytes(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.MethodDescriptorProto_Name:
+ md.L0.FullName = nb.AppendFullName(pd.FullName(), v)
+ case fieldnum.MethodDescriptorProto_InputType:
+ md.L1.Input = PlaceholderMessage(nb.MakeFullName(v))
+ case fieldnum.MethodDescriptorProto_OutputType:
+ md.L1.Output = PlaceholderMessage(nb.MakeFullName(v))
+ case fieldnum.MethodDescriptorProto_Options:
+ rawOptions = appendOptions(rawOptions, v)
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+ md.L1.Options = pf.builder.optionsUnmarshaler(descopts.Method, rawOptions)
+}
+
+// appendOptions appends src to dst, where the returned slice is never nil.
+// This is necessary to distinguish between empty and unpopulated options.
+func appendOptions(dst, src []byte) []byte {
+ if dst == nil {
+ dst = []byte{}
+ }
+ return append(dst, src...)
+}
+
+func (db *DescBuilder) optionsUnmarshaler(p pref.ProtoMessage, b []byte) func() pref.ProtoMessage {
+ if b == nil {
+ return nil
+ }
+ return func() pref.ProtoMessage {
+ p := reflect.New(reflect.TypeOf(p).Elem()).Interface().(pref.ProtoMessage)
+ if err := (proto.UnmarshalOptions{
+ AllowPartial: true,
+ Resolver: db.TypeResolver,
+ }).Unmarshal(b, p); err != nil {
+ panic(err)
+ }
+ return p
+ }
+}
diff --git a/internal/filedesc/desc_list.go b/internal/filedesc/desc_list.go
new file mode 100644
index 0000000..bbded60
--- /dev/null
+++ b/internal/filedesc/desc_list.go
@@ -0,0 +1,189 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package filedesc
+
+import (
+ "fmt"
+ "sort"
+ "sync"
+
+ "google.golang.org/protobuf/internal/descfmt"
+ "google.golang.org/protobuf/internal/pragma"
+ pref "google.golang.org/protobuf/reflect/protoreflect"
+)
+
+type FileImports []pref.FileImport
+
+func (p *FileImports) Len() int { return len(*p) }
+func (p *FileImports) Get(i int) pref.FileImport { return (*p)[i] }
+func (p *FileImports) Format(s fmt.State, r rune) { descfmt.FormatList(s, r, p) }
+func (p *FileImports) ProtoInternal(pragma.DoNotImplement) {}
+
+type Names struct {
+ List []pref.Name
+ once sync.Once
+ has map[pref.Name]struct{} // protected by once
+}
+
+func (p *Names) Len() int { return len(p.List) }
+func (p *Names) Get(i int) pref.Name { return p.List[i] }
+func (p *Names) Has(s pref.Name) bool {
+ p.once.Do(func() {
+ if len(p.List) > 0 {
+ p.has = make(map[pref.Name]struct{}, len(p.List))
+ for _, s := range p.List {
+ p.has[s] = struct{}{}
+ }
+ }
+ })
+ _, ok := p.has[s]
+ return ok
+}
+func (p *Names) Format(s fmt.State, r rune) { descfmt.FormatList(s, r, p) }
+func (p *Names) ProtoInternal(pragma.DoNotImplement) {}
+
+type EnumRanges struct {
+ List [][2]pref.EnumNumber // start inclusive; end inclusive
+ once sync.Once
+ sorted [][2]pref.EnumNumber // protected by once
+ has map[pref.EnumNumber]struct{} // protected by once
+}
+
+func (p *EnumRanges) Len() int { return len(p.List) }
+func (p *EnumRanges) Get(i int) [2]pref.EnumNumber { return p.List[i] }
+func (p *EnumRanges) Has(n pref.EnumNumber) bool {
+ p.once.Do(func() {
+ for _, r := range p.List {
+ if r[0] == r[1]-0 {
+ if p.has == nil {
+ p.has = make(map[pref.EnumNumber]struct{}, len(p.List))
+ }
+ p.has[r[0]] = struct{}{}
+ } else {
+ p.sorted = append(p.sorted, r)
+ }
+ }
+ sort.Slice(p.sorted, func(i, j int) bool {
+ return p.sorted[i][0] < p.sorted[j][0]
+ })
+ })
+ if _, ok := p.has[n]; ok {
+ return true
+ }
+ for ls := p.sorted; len(ls) > 0; {
+ i := len(ls) / 2
+ switch r := ls[i]; {
+ case n < r[0]:
+ ls = ls[:i] // search lower
+ case n > r[1]:
+ ls = ls[i+1:] // search upper
+ default:
+ return true
+ }
+ }
+ return false
+}
+func (p *EnumRanges) Format(s fmt.State, r rune) { descfmt.FormatList(s, r, p) }
+func (p *EnumRanges) ProtoInternal(pragma.DoNotImplement) {}
+
+type FieldRanges struct {
+ List [][2]pref.FieldNumber // start inclusive; end exclusive
+ once sync.Once
+ sorted [][2]pref.FieldNumber // protected by once
+ has map[pref.FieldNumber]struct{} // protected by once
+}
+
+func (p *FieldRanges) Len() int { return len(p.List) }
+func (p *FieldRanges) Get(i int) [2]pref.FieldNumber { return p.List[i] }
+func (p *FieldRanges) Has(n pref.FieldNumber) bool {
+ p.once.Do(func() {
+ for _, r := range p.List {
+ if r[0] == r[1]-1 {
+ if p.has == nil {
+ p.has = make(map[pref.FieldNumber]struct{}, len(p.List))
+ }
+ p.has[r[0]] = struct{}{}
+ } else {
+ p.sorted = append(p.sorted, r)
+ }
+ }
+ sort.Slice(p.sorted, func(i, j int) bool {
+ return p.sorted[i][0] < p.sorted[j][0]
+ })
+ })
+ if _, ok := p.has[n]; ok {
+ return true
+ }
+ for ls := p.sorted; len(ls) > 0; {
+ i := len(ls) / 2
+ switch r := ls[i]; {
+ case n < r[0]:
+ ls = ls[:i] // search lower
+ case n >= r[1]:
+ ls = ls[i+1:] // search higher
+ default:
+ return true
+ }
+ }
+ return false
+}
+func (p *FieldRanges) Format(s fmt.State, r rune) { descfmt.FormatList(s, r, p) }
+func (p *FieldRanges) ProtoInternal(pragma.DoNotImplement) {}
+
+type FieldNumbers struct {
+ List []pref.FieldNumber
+ once sync.Once
+ has map[pref.FieldNumber]struct{} // protected by once
+}
+
+func (p *FieldNumbers) Len() int { return len(p.List) }
+func (p *FieldNumbers) Get(i int) pref.FieldNumber { return p.List[i] }
+func (p *FieldNumbers) Has(n pref.FieldNumber) bool {
+ p.once.Do(func() {
+ if len(p.List) > 0 {
+ p.has = make(map[pref.FieldNumber]struct{}, len(p.List))
+ for _, n := range p.List {
+ p.has[n] = struct{}{}
+ }
+ }
+ })
+ _, ok := p.has[n]
+ return ok
+}
+func (p *FieldNumbers) Format(s fmt.State, r rune) { descfmt.FormatList(s, r, p) }
+func (p *FieldNumbers) ProtoInternal(pragma.DoNotImplement) {}
+
+type OneofFields struct {
+ List []pref.FieldDescriptor
+ once sync.Once
+ byName map[pref.Name]pref.FieldDescriptor // protected by once
+ byJSON map[string]pref.FieldDescriptor // protected by once
+ byNum map[pref.FieldNumber]pref.FieldDescriptor // protected by once
+}
+
+func (p *OneofFields) Len() int { return len(p.List) }
+func (p *OneofFields) Get(i int) pref.FieldDescriptor { return p.List[i] }
+func (p *OneofFields) ByName(s pref.Name) pref.FieldDescriptor { return p.lazyInit().byName[s] }
+func (p *OneofFields) ByJSONName(s string) pref.FieldDescriptor { return p.lazyInit().byJSON[s] }
+func (p *OneofFields) ByNumber(n pref.FieldNumber) pref.FieldDescriptor { return p.lazyInit().byNum[n] }
+func (p *OneofFields) Format(s fmt.State, r rune) { descfmt.FormatList(s, r, p) }
+func (p *OneofFields) ProtoInternal(pragma.DoNotImplement) {}
+
+func (p *OneofFields) lazyInit() *OneofFields {
+ p.once.Do(func() {
+ if len(p.List) > 0 {
+ p.byName = make(map[pref.Name]pref.FieldDescriptor, len(p.List))
+ p.byJSON = make(map[string]pref.FieldDescriptor, len(p.List))
+ p.byNum = make(map[pref.FieldNumber]pref.FieldDescriptor, len(p.List))
+ for _, f := range p.List {
+ // Field names and numbers are guaranteed to be unique.
+ p.byName[f.Name()] = f
+ p.byJSON[f.JSONName()] = f
+ p.byNum[f.Number()] = f
+ }
+ }
+ })
+ return p
+}
diff --git a/internal/filedesc/desc_list_gen.go b/internal/filedesc/desc_list_gen.go
new file mode 100644
index 0000000..d875920
--- /dev/null
+++ b/internal/filedesc/desc_list_gen.go
@@ -0,0 +1,345 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style.
+// license that can be found in the LICENSE file.
+
+// Code generated by generate-types. DO NOT EDIT.
+
+package filedesc
+
+import (
+ "fmt"
+ "sync"
+
+ "google.golang.org/protobuf/internal/descfmt"
+ "google.golang.org/protobuf/internal/pragma"
+ "google.golang.org/protobuf/reflect/protoreflect"
+)
+
+type Enums struct {
+ List []Enum
+ once sync.Once
+ byName map[protoreflect.Name]*Enum // protected by once
+}
+
+func (p *Enums) Len() int {
+ return len(p.List)
+}
+func (p *Enums) Get(i int) protoreflect.EnumDescriptor {
+ return &p.List[i]
+}
+func (p *Enums) ByName(s protoreflect.Name) protoreflect.EnumDescriptor {
+ if d := p.lazyInit().byName[s]; d != nil {
+ return d
+ }
+ return nil
+}
+func (p *Enums) Format(s fmt.State, r rune) {
+ descfmt.FormatList(s, r, p)
+}
+func (p *Enums) ProtoInternal(pragma.DoNotImplement) {}
+func (p *Enums) lazyInit() *Enums {
+ p.once.Do(func() {
+ if len(p.List) > 0 {
+ p.byName = make(map[protoreflect.Name]*Enum, len(p.List))
+ for i := range p.List {
+ d := &p.List[i]
+ if _, ok := p.byName[d.Name()]; !ok {
+ p.byName[d.Name()] = d
+ }
+ }
+ }
+ })
+ return p
+}
+
+type EnumValues struct {
+ List []EnumValue
+ once sync.Once
+ byName map[protoreflect.Name]*EnumValue // protected by once
+ byNum map[protoreflect.EnumNumber]*EnumValue // protected by once
+}
+
+func (p *EnumValues) Len() int {
+ return len(p.List)
+}
+func (p *EnumValues) Get(i int) protoreflect.EnumValueDescriptor {
+ return &p.List[i]
+}
+func (p *EnumValues) ByName(s protoreflect.Name) protoreflect.EnumValueDescriptor {
+ if d := p.lazyInit().byName[s]; d != nil {
+ return d
+ }
+ return nil
+}
+func (p *EnumValues) ByNumber(n protoreflect.EnumNumber) protoreflect.EnumValueDescriptor {
+ if d := p.lazyInit().byNum[n]; d != nil {
+ return d
+ }
+ return nil
+}
+func (p *EnumValues) Format(s fmt.State, r rune) {
+ descfmt.FormatList(s, r, p)
+}
+func (p *EnumValues) ProtoInternal(pragma.DoNotImplement) {}
+func (p *EnumValues) lazyInit() *EnumValues {
+ p.once.Do(func() {
+ if len(p.List) > 0 {
+ p.byName = make(map[protoreflect.Name]*EnumValue, len(p.List))
+ p.byNum = make(map[protoreflect.EnumNumber]*EnumValue, len(p.List))
+ for i := range p.List {
+ d := &p.List[i]
+ if _, ok := p.byName[d.Name()]; !ok {
+ p.byName[d.Name()] = d
+ }
+ if _, ok := p.byNum[d.Number()]; !ok {
+ p.byNum[d.Number()] = d
+ }
+ }
+ }
+ })
+ return p
+}
+
+type Messages struct {
+ List []Message
+ once sync.Once
+ byName map[protoreflect.Name]*Message // protected by once
+}
+
+func (p *Messages) Len() int {
+ return len(p.List)
+}
+func (p *Messages) Get(i int) protoreflect.MessageDescriptor {
+ return &p.List[i]
+}
+func (p *Messages) ByName(s protoreflect.Name) protoreflect.MessageDescriptor {
+ if d := p.lazyInit().byName[s]; d != nil {
+ return d
+ }
+ return nil
+}
+func (p *Messages) Format(s fmt.State, r rune) {
+ descfmt.FormatList(s, r, p)
+}
+func (p *Messages) ProtoInternal(pragma.DoNotImplement) {}
+func (p *Messages) lazyInit() *Messages {
+ p.once.Do(func() {
+ if len(p.List) > 0 {
+ p.byName = make(map[protoreflect.Name]*Message, len(p.List))
+ for i := range p.List {
+ d := &p.List[i]
+ if _, ok := p.byName[d.Name()]; !ok {
+ p.byName[d.Name()] = d
+ }
+ }
+ }
+ })
+ return p
+}
+
+type Fields struct {
+ List []Field
+ once sync.Once
+ byName map[protoreflect.Name]*Field // protected by once
+ byJSON map[string]*Field // protected by once
+ byNum map[protoreflect.FieldNumber]*Field // protected by once
+}
+
+func (p *Fields) Len() int {
+ return len(p.List)
+}
+func (p *Fields) Get(i int) protoreflect.FieldDescriptor {
+ return &p.List[i]
+}
+func (p *Fields) ByName(s protoreflect.Name) protoreflect.FieldDescriptor {
+ if d := p.lazyInit().byName[s]; d != nil {
+ return d
+ }
+ return nil
+}
+func (p *Fields) ByJSONName(s string) protoreflect.FieldDescriptor {
+ if d := p.lazyInit().byJSON[s]; d != nil {
+ return d
+ }
+ return nil
+}
+func (p *Fields) ByNumber(n protoreflect.FieldNumber) protoreflect.FieldDescriptor {
+ if d := p.lazyInit().byNum[n]; d != nil {
+ return d
+ }
+ return nil
+}
+func (p *Fields) Format(s fmt.State, r rune) {
+ descfmt.FormatList(s, r, p)
+}
+func (p *Fields) ProtoInternal(pragma.DoNotImplement) {}
+func (p *Fields) lazyInit() *Fields {
+ p.once.Do(func() {
+ if len(p.List) > 0 {
+ p.byName = make(map[protoreflect.Name]*Field, len(p.List))
+ p.byJSON = make(map[string]*Field, len(p.List))
+ p.byNum = make(map[protoreflect.FieldNumber]*Field, len(p.List))
+ for i := range p.List {
+ d := &p.List[i]
+ if _, ok := p.byName[d.Name()]; !ok {
+ p.byName[d.Name()] = d
+ }
+ if _, ok := p.byJSON[d.JSONName()]; !ok {
+ p.byJSON[d.JSONName()] = d
+ }
+ if _, ok := p.byNum[d.Number()]; !ok {
+ p.byNum[d.Number()] = d
+ }
+ }
+ }
+ })
+ return p
+}
+
+type Oneofs struct {
+ List []Oneof
+ once sync.Once
+ byName map[protoreflect.Name]*Oneof // protected by once
+}
+
+func (p *Oneofs) Len() int {
+ return len(p.List)
+}
+func (p *Oneofs) Get(i int) protoreflect.OneofDescriptor {
+ return &p.List[i]
+}
+func (p *Oneofs) ByName(s protoreflect.Name) protoreflect.OneofDescriptor {
+ if d := p.lazyInit().byName[s]; d != nil {
+ return d
+ }
+ return nil
+}
+func (p *Oneofs) Format(s fmt.State, r rune) {
+ descfmt.FormatList(s, r, p)
+}
+func (p *Oneofs) ProtoInternal(pragma.DoNotImplement) {}
+func (p *Oneofs) lazyInit() *Oneofs {
+ p.once.Do(func() {
+ if len(p.List) > 0 {
+ p.byName = make(map[protoreflect.Name]*Oneof, len(p.List))
+ for i := range p.List {
+ d := &p.List[i]
+ if _, ok := p.byName[d.Name()]; !ok {
+ p.byName[d.Name()] = d
+ }
+ }
+ }
+ })
+ return p
+}
+
+type Extensions struct {
+ List []Extension
+ once sync.Once
+ byName map[protoreflect.Name]*Extension // protected by once
+}
+
+func (p *Extensions) Len() int {
+ return len(p.List)
+}
+func (p *Extensions) Get(i int) protoreflect.ExtensionDescriptor {
+ return &p.List[i]
+}
+func (p *Extensions) ByName(s protoreflect.Name) protoreflect.ExtensionDescriptor {
+ if d := p.lazyInit().byName[s]; d != nil {
+ return d
+ }
+ return nil
+}
+func (p *Extensions) Format(s fmt.State, r rune) {
+ descfmt.FormatList(s, r, p)
+}
+func (p *Extensions) ProtoInternal(pragma.DoNotImplement) {}
+func (p *Extensions) lazyInit() *Extensions {
+ p.once.Do(func() {
+ if len(p.List) > 0 {
+ p.byName = make(map[protoreflect.Name]*Extension, len(p.List))
+ for i := range p.List {
+ d := &p.List[i]
+ if _, ok := p.byName[d.Name()]; !ok {
+ p.byName[d.Name()] = d
+ }
+ }
+ }
+ })
+ return p
+}
+
+type Services struct {
+ List []Service
+ once sync.Once
+ byName map[protoreflect.Name]*Service // protected by once
+}
+
+func (p *Services) Len() int {
+ return len(p.List)
+}
+func (p *Services) Get(i int) protoreflect.ServiceDescriptor {
+ return &p.List[i]
+}
+func (p *Services) ByName(s protoreflect.Name) protoreflect.ServiceDescriptor {
+ if d := p.lazyInit().byName[s]; d != nil {
+ return d
+ }
+ return nil
+}
+func (p *Services) Format(s fmt.State, r rune) {
+ descfmt.FormatList(s, r, p)
+}
+func (p *Services) ProtoInternal(pragma.DoNotImplement) {}
+func (p *Services) lazyInit() *Services {
+ p.once.Do(func() {
+ if len(p.List) > 0 {
+ p.byName = make(map[protoreflect.Name]*Service, len(p.List))
+ for i := range p.List {
+ d := &p.List[i]
+ if _, ok := p.byName[d.Name()]; !ok {
+ p.byName[d.Name()] = d
+ }
+ }
+ }
+ })
+ return p
+}
+
+type Methods struct {
+ List []Method
+ once sync.Once
+ byName map[protoreflect.Name]*Method // protected by once
+}
+
+func (p *Methods) Len() int {
+ return len(p.List)
+}
+func (p *Methods) Get(i int) protoreflect.MethodDescriptor {
+ return &p.List[i]
+}
+func (p *Methods) ByName(s protoreflect.Name) protoreflect.MethodDescriptor {
+ if d := p.lazyInit().byName[s]; d != nil {
+ return d
+ }
+ return nil
+}
+func (p *Methods) Format(s fmt.State, r rune) {
+ descfmt.FormatList(s, r, p)
+}
+func (p *Methods) ProtoInternal(pragma.DoNotImplement) {}
+func (p *Methods) lazyInit() *Methods {
+ p.once.Do(func() {
+ if len(p.List) > 0 {
+ p.byName = make(map[protoreflect.Name]*Method, len(p.List))
+ for i := range p.List {
+ d := &p.List[i]
+ if _, ok := p.byName[d.Name()]; !ok {
+ p.byName[d.Name()] = d
+ }
+ }
+ }
+ })
+ return p
+}
diff --git a/internal/filedesc/desc_test.go b/internal/filedesc/desc_test.go
new file mode 100644
index 0000000..b6e0f7d
--- /dev/null
+++ b/internal/filedesc/desc_test.go
@@ -0,0 +1,772 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package filedesc_test
+
+import (
+ "fmt"
+ "reflect"
+ "regexp"
+ "strconv"
+ "strings"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ detrand "google.golang.org/protobuf/internal/detrand"
+ "google.golang.org/protobuf/internal/filedesc"
+ scalar "google.golang.org/protobuf/internal/scalar"
+ "google.golang.org/protobuf/proto"
+ pdesc "google.golang.org/protobuf/reflect/protodesc"
+ pref "google.golang.org/protobuf/reflect/protoreflect"
+
+ "google.golang.org/protobuf/types/descriptorpb"
+)
+
+func init() {
+ // Disable detrand to enable direct comparisons on outputs.
+ detrand.Disable()
+}
+
+// TODO: Test protodesc.NewFile with imported files.
+
+func TestFile(t *testing.T) {
+ f1 := &descriptorpb.FileDescriptorProto{
+ Syntax: scalar.String("proto2"),
+ Name: scalar.String("path/to/file.proto"),
+ Package: scalar.String("test"),
+ Options: &descriptorpb.FileOptions{Deprecated: scalar.Bool(true)},
+ MessageType: []*descriptorpb.DescriptorProto{{
+ Name: scalar.String("A"),
+ Options: &descriptorpb.MessageOptions{
+ MapEntry: scalar.Bool(true),
+ Deprecated: scalar.Bool(true),
+ },
+ Field: []*descriptorpb.FieldDescriptorProto{{
+ Name: scalar.String("key"),
+ Number: scalar.Int32(1),
+ Options: &descriptorpb.FieldOptions{Deprecated: scalar.Bool(true)},
+ Label: descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+ Type: descriptorpb.FieldDescriptorProto_Type(pref.StringKind).Enum(),
+ }, {
+ Name: scalar.String("value"),
+ Number: scalar.Int32(2),
+ Label: descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+ Type: descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+ TypeName: scalar.String(".test.B"),
+ }},
+ }, {
+ Name: scalar.String("B"),
+ Field: []*descriptorpb.FieldDescriptorProto{{
+ Name: scalar.String("field_one"),
+ Number: scalar.Int32(1),
+ Label: descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+ Type: descriptorpb.FieldDescriptorProto_Type(pref.StringKind).Enum(),
+ DefaultValue: scalar.String("hello, \"world!\"\n"),
+ OneofIndex: scalar.Int32(0),
+ }, {
+ Name: scalar.String("field_two"),
+ JsonName: scalar.String("Field2"),
+ Number: scalar.Int32(2),
+ Label: descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+ Type: descriptorpb.FieldDescriptorProto_Type(pref.EnumKind).Enum(),
+ DefaultValue: scalar.String("BAR"),
+ TypeName: scalar.String(".test.E1"),
+ OneofIndex: scalar.Int32(1),
+ }, {
+ Name: scalar.String("field_three"),
+ Number: scalar.Int32(3),
+ Label: descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+ Type: descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+ TypeName: scalar.String(".test.C"),
+ OneofIndex: scalar.Int32(1),
+ }, {
+ Name: scalar.String("field_four"),
+ JsonName: scalar.String("Field4"),
+ Number: scalar.Int32(4),
+ Label: descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+ Type: descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+ TypeName: scalar.String(".test.A"),
+ }, {
+ Name: scalar.String("field_five"),
+ Number: scalar.Int32(5),
+ Label: descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+ Type: descriptorpb.FieldDescriptorProto_Type(pref.Int32Kind).Enum(),
+ Options: &descriptorpb.FieldOptions{Packed: scalar.Bool(true)},
+ }, {
+ Name: scalar.String("field_six"),
+ Number: scalar.Int32(6),
+ Label: descriptorpb.FieldDescriptorProto_Label(pref.Required).Enum(),
+ Type: descriptorpb.FieldDescriptorProto_Type(pref.BytesKind).Enum(),
+ }},
+ OneofDecl: []*descriptorpb.OneofDescriptorProto{
+ {
+ Name: scalar.String("O1"),
+ Options: &descriptorpb.OneofOptions{
+ UninterpretedOption: []*descriptorpb.UninterpretedOption{
+ {StringValue: []byte("option")},
+ },
+ },
+ },
+ {Name: scalar.String("O2")},
+ },
+ ReservedName: []string{"fizz", "buzz"},
+ ReservedRange: []*descriptorpb.DescriptorProto_ReservedRange{
+ {Start: scalar.Int32(100), End: scalar.Int32(200)},
+ {Start: scalar.Int32(300), End: scalar.Int32(301)},
+ },
+ ExtensionRange: []*descriptorpb.DescriptorProto_ExtensionRange{
+ {Start: scalar.Int32(1000), End: scalar.Int32(2000)},
+ {Start: scalar.Int32(3000), End: scalar.Int32(3001), Options: new(descriptorpb.ExtensionRangeOptions)},
+ },
+ }, {
+ Name: scalar.String("C"),
+ NestedType: []*descriptorpb.DescriptorProto{{
+ Name: scalar.String("A"),
+ Field: []*descriptorpb.FieldDescriptorProto{{
+ Name: scalar.String("F"),
+ Number: scalar.Int32(1),
+ Label: descriptorpb.FieldDescriptorProto_Label(pref.Required).Enum(),
+ Type: descriptorpb.FieldDescriptorProto_Type(pref.BytesKind).Enum(),
+ DefaultValue: scalar.String(`dead\276\357`),
+ }},
+ }},
+ EnumType: []*descriptorpb.EnumDescriptorProto{{
+ Name: scalar.String("E1"),
+ Value: []*descriptorpb.EnumValueDescriptorProto{
+ {Name: scalar.String("FOO"), Number: scalar.Int32(0)},
+ {Name: scalar.String("BAR"), Number: scalar.Int32(1)},
+ },
+ }},
+ Extension: []*descriptorpb.FieldDescriptorProto{{
+ Name: scalar.String("X"),
+ Number: scalar.Int32(1000),
+ Label: descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+ Type: descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+ TypeName: scalar.String(".test.C"),
+ Extendee: scalar.String(".test.B"),
+ }},
+ }},
+ EnumType: []*descriptorpb.EnumDescriptorProto{{
+ Name: scalar.String("E1"),
+ Options: &descriptorpb.EnumOptions{Deprecated: scalar.Bool(true)},
+ Value: []*descriptorpb.EnumValueDescriptorProto{
+ {
+ Name: scalar.String("FOO"),
+ Number: scalar.Int32(0),
+ Options: &descriptorpb.EnumValueOptions{Deprecated: scalar.Bool(true)},
+ },
+ {Name: scalar.String("BAR"), Number: scalar.Int32(1)},
+ },
+ ReservedName: []string{"FIZZ", "BUZZ"},
+ ReservedRange: []*descriptorpb.EnumDescriptorProto_EnumReservedRange{
+ {Start: scalar.Int32(10), End: scalar.Int32(19)},
+ {Start: scalar.Int32(30), End: scalar.Int32(30)},
+ },
+ }},
+ Extension: []*descriptorpb.FieldDescriptorProto{{
+ Name: scalar.String("X"),
+ Number: scalar.Int32(1000),
+ Label: descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+ Type: descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+ Options: &descriptorpb.FieldOptions{Packed: scalar.Bool(true)},
+ TypeName: scalar.String(".test.C"),
+ Extendee: scalar.String(".test.B"),
+ }},
+ Service: []*descriptorpb.ServiceDescriptorProto{{
+ Name: scalar.String("S"),
+ Options: &descriptorpb.ServiceOptions{Deprecated: scalar.Bool(true)},
+ Method: []*descriptorpb.MethodDescriptorProto{{
+ Name: scalar.String("M"),
+ InputType: scalar.String(".test.A"),
+ OutputType: scalar.String(".test.C.A"),
+ ClientStreaming: scalar.Bool(true),
+ ServerStreaming: scalar.Bool(true),
+ Options: &descriptorpb.MethodOptions{Deprecated: scalar.Bool(true)},
+ }},
+ }},
+ }
+ fd1, err := pdesc.NewFile(f1, nil)
+ if err != nil {
+ t.Fatalf("protodesc.NewFile() error: %v", err)
+ }
+
+ b, err := proto.Marshal(f1)
+ if err != nil {
+ t.Fatalf("proto.Marshal() error: %v", err)
+ }
+ fd2 := filedesc.DescBuilder{RawDescriptor: b}.Build().File
+
+ tests := []struct {
+ name string
+ desc pref.FileDescriptor
+ }{
+ {"protodesc.NewFile", fd1},
+ {"filedesc.DescBuilder.Build", fd2},
+ }
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ // Run sub-tests in parallel to induce potential races.
+ for i := 0; i < 2; i++ {
+ t.Run("Accessors", func(t *testing.T) { t.Parallel(); testFileAccessors(t, tt.desc) })
+ t.Run("Format", func(t *testing.T) { t.Parallel(); testFileFormat(t, tt.desc) })
+ }
+ })
+ }
+}
+
+func testFileAccessors(t *testing.T, fd pref.FileDescriptor) {
+ // Represent the descriptor as a map where each key is an accessor method
+ // and the value is either the wanted tail value or another accessor map.
+ type M = map[string]interface{}
+ want := M{
+ "Parent": nil,
+ "Index": 0,
+ "Syntax": pref.Proto2,
+ "Name": pref.Name("test"),
+ "FullName": pref.FullName("test"),
+ "Path": "path/to/file.proto",
+ "Package": pref.FullName("test"),
+ "IsPlaceholder": false,
+ "Options": &descriptorpb.FileOptions{Deprecated: scalar.Bool(true)},
+ "Messages": M{
+ "Len": 3,
+ "Get:0": M{
+ "Parent": M{"FullName": pref.FullName("test")},
+ "Index": 0,
+ "Syntax": pref.Proto2,
+ "Name": pref.Name("A"),
+ "FullName": pref.FullName("test.A"),
+ "IsPlaceholder": false,
+ "IsMapEntry": true,
+ "Options": &descriptorpb.MessageOptions{
+ MapEntry: scalar.Bool(true),
+ Deprecated: scalar.Bool(true),
+ },
+ "Fields": M{
+ "Len": 2,
+ "ByNumber:1": M{
+ "Parent": M{"FullName": pref.FullName("test.A")},
+ "Index": 0,
+ "Name": pref.Name("key"),
+ "FullName": pref.FullName("test.A.key"),
+ "Number": pref.FieldNumber(1),
+ "Cardinality": pref.Optional,
+ "Kind": pref.StringKind,
+ "Options": &descriptorpb.FieldOptions{Deprecated: scalar.Bool(true)},
+ "HasJSONName": false,
+ "JSONName": "key",
+ "IsPacked": false,
+ "IsList": false,
+ "IsMap": false,
+ "IsExtension": false,
+ "IsWeak": false,
+ "Default": "",
+ "ContainingOneof": nil,
+ "ContainingMessage": M{"FullName": pref.FullName("test.A")},
+ "Message": nil,
+ "Enum": nil,
+ },
+ "ByNumber:2": M{
+ "Parent": M{"FullName": pref.FullName("test.A")},
+ "Index": 1,
+ "Name": pref.Name("value"),
+ "FullName": pref.FullName("test.A.value"),
+ "Number": pref.FieldNumber(2),
+ "Cardinality": pref.Optional,
+ "Kind": pref.MessageKind,
+ "JSONName": "value",
+ "IsPacked": false,
+ "IsList": false,
+ "IsMap": false,
+ "IsExtension": false,
+ "IsWeak": false,
+ "Default": nil,
+ "ContainingOneof": nil,
+ "ContainingMessage": M{"FullName": pref.FullName("test.A")},
+ "Message": M{"FullName": pref.FullName("test.B"), "IsPlaceholder": false},
+ "Enum": nil,
+ },
+ "ByNumber:3": nil,
+ },
+ "Oneofs": M{"Len": 0},
+ "RequiredNumbers": M{"Len": 0},
+ "ExtensionRanges": M{"Len": 0},
+ "Messages": M{"Len": 0},
+ "Enums": M{"Len": 0},
+ "Extensions": M{"Len": 0},
+ },
+ "ByName:B": M{
+ "Name": pref.Name("B"),
+ "Index": 1,
+ "Fields": M{
+ "Len": 6,
+ "ByJSONName:field_one": nil,
+ "ByJSONName:fieldOne": M{
+ "Name": pref.Name("field_one"),
+ "Index": 0,
+ "JSONName": "fieldOne",
+ "Default": "hello, \"world!\"\n",
+ "ContainingOneof": M{"Name": pref.Name("O1"), "IsPlaceholder": false},
+ "ContainingMessage": M{"FullName": pref.FullName("test.B")},
+ },
+ "ByJSONName:fieldTwo": nil,
+ "ByJSONName:Field2": M{
+ "Name": pref.Name("field_two"),
+ "Index": 1,
+ "HasJSONName": true,
+ "JSONName": "Field2",
+ "Default": pref.EnumNumber(1),
+ "ContainingOneof": M{"Name": pref.Name("O2"), "IsPlaceholder": false},
+ },
+ "ByName:fieldThree": nil,
+ "ByName:field_three": M{
+ "IsExtension": false,
+ "IsMap": false,
+ "MapKey": nil,
+ "MapValue": nil,
+ "Message": M{"FullName": pref.FullName("test.C"), "IsPlaceholder": false},
+ "ContainingOneof": M{"Name": pref.Name("O2"), "IsPlaceholder": false},
+ "ContainingMessage": M{"FullName": pref.FullName("test.B")},
+ },
+ "ByNumber:12": nil,
+ "ByNumber:4": M{
+ "Cardinality": pref.Repeated,
+ "IsExtension": false,
+ "IsList": false,
+ "IsMap": true,
+ "MapKey": M{"Kind": pref.StringKind},
+ "MapValue": M{"Kind": pref.MessageKind, "Message": M{"FullName": pref.FullName("test.B")}},
+ "Default": nil,
+ "Message": M{"FullName": pref.FullName("test.A"), "IsPlaceholder": false},
+ },
+ "ByNumber:5": M{
+ "Cardinality": pref.Repeated,
+ "Kind": pref.Int32Kind,
+ "IsPacked": true,
+ "IsList": true,
+ "IsMap": false,
+ "Default": int32(0),
+ },
+ "ByNumber:6": M{
+ "Cardinality": pref.Required,
+ "Default": []byte(nil),
+ "ContainingOneof": nil,
+ },
+ },
+ "Oneofs": M{
+ "Len": 2,
+ "ByName:O0": nil,
+ "ByName:O1": M{
+ "FullName": pref.FullName("test.B.O1"),
+ "Index": 0,
+ "Options": &descriptorpb.OneofOptions{
+ UninterpretedOption: []*descriptorpb.UninterpretedOption{
+ {StringValue: []byte("option")},
+ },
+ },
+ "Fields": M{
+ "Len": 1,
+ "Get:0": M{"FullName": pref.FullName("test.B.field_one")},
+ },
+ },
+ "Get:1": M{
+ "FullName": pref.FullName("test.B.O2"),
+ "Index": 1,
+ "Fields": M{
+ "Len": 2,
+ "ByName:field_two": M{"Name": pref.Name("field_two")},
+ "Get:1": M{"Name": pref.Name("field_three")},
+ },
+ },
+ },
+ "ReservedNames": M{
+ "Len": 2,
+ "Get:0": pref.Name("fizz"),
+ "Has:buzz": true,
+ "Has:noexist": false,
+ },
+ "ReservedRanges": M{
+ "Len": 2,
+ "Get:0": [2]pref.FieldNumber{100, 200},
+ "Has:99": false,
+ "Has:100": true,
+ "Has:150": true,
+ "Has:199": true,
+ "Has:200": false,
+ "Has:300": true,
+ "Has:301": false,
+ },
+ "RequiredNumbers": M{
+ "Len": 1,
+ "Get:0": pref.FieldNumber(6),
+ "Has:1": false,
+ "Has:6": true,
+ },
+ "ExtensionRanges": M{
+ "Len": 2,
+ "Get:0": [2]pref.FieldNumber{1000, 2000},
+ "Has:999": false,
+ "Has:1000": true,
+ "Has:1500": true,
+ "Has:1999": true,
+ "Has:2000": false,
+ "Has:3000": true,
+ "Has:3001": false,
+ },
+ "ExtensionRangeOptions:0": (*descriptorpb.ExtensionRangeOptions)(nil),
+ "ExtensionRangeOptions:1": new(descriptorpb.ExtensionRangeOptions),
+ },
+ "Get:2": M{
+ "Name": pref.Name("C"),
+ "Index": 2,
+ "Messages": M{
+ "Len": 1,
+ "Get:0": M{"FullName": pref.FullName("test.C.A")},
+ },
+ "Enums": M{
+ "Len": 1,
+ "Get:0": M{"FullName": pref.FullName("test.C.E1")},
+ },
+ "Extensions": M{
+ "Len": 1,
+ "Get:0": M{"FullName": pref.FullName("test.C.X")},
+ },
+ },
+ },
+ "Enums": M{
+ "Len": 1,
+ "Get:0": M{
+ "Name": pref.Name("E1"),
+ "Options": &descriptorpb.EnumOptions{Deprecated: scalar.Bool(true)},
+ "Values": M{
+ "Len": 2,
+ "ByName:Foo": nil,
+ "ByName:FOO": M{
+ "FullName": pref.FullName("test.FOO"),
+ "Options": &descriptorpb.EnumValueOptions{Deprecated: scalar.Bool(true)},
+ },
+ "ByNumber:2": nil,
+ "ByNumber:1": M{"FullName": pref.FullName("test.BAR")},
+ },
+ "ReservedNames": M{
+ "Len": 2,
+ "Get:0": pref.Name("FIZZ"),
+ "Has:BUZZ": true,
+ "Has:NOEXIST": false,
+ },
+ "ReservedRanges": M{
+ "Len": 2,
+ "Get:0": [2]pref.EnumNumber{10, 19},
+ "Has:9": false,
+ "Has:10": true,
+ "Has:15": true,
+ "Has:19": true,
+ "Has:20": false,
+ "Has:30": true,
+ "Has:31": false,
+ },
+ },
+ },
+ "Extensions": M{
+ "Len": 1,
+ "ByName:X": M{
+ "Name": pref.Name("X"),
+ "Number": pref.FieldNumber(1000),
+ "Cardinality": pref.Repeated,
+ "Kind": pref.MessageKind,
+ "IsExtension": true,
+ "IsPacked": true,
+ "IsList": true,
+ "IsMap": false,
+ "MapKey": nil,
+ "MapValue": nil,
+ "ContainingOneof": nil,
+ "ContainingMessage": M{"FullName": pref.FullName("test.B"), "IsPlaceholder": false},
+ "Message": M{"FullName": pref.FullName("test.C"), "IsPlaceholder": false},
+ "Options": &descriptorpb.FieldOptions{Packed: scalar.Bool(true)},
+ },
+ },
+ "Services": M{
+ "Len": 1,
+ "ByName:s": nil,
+ "ByName:S": M{
+ "Parent": M{"FullName": pref.FullName("test")},
+ "Name": pref.Name("S"),
+ "FullName": pref.FullName("test.S"),
+ "Options": &descriptorpb.ServiceOptions{Deprecated: scalar.Bool(true)},
+ "Methods": M{
+ "Len": 1,
+ "Get:0": M{
+ "Parent": M{"FullName": pref.FullName("test.S")},
+ "Name": pref.Name("M"),
+ "FullName": pref.FullName("test.S.M"),
+ "Input": M{"FullName": pref.FullName("test.A"), "IsPlaceholder": false},
+ "Output": M{"FullName": pref.FullName("test.C.A"), "IsPlaceholder": false},
+ "IsStreamingClient": true,
+ "IsStreamingServer": true,
+ "Options": &descriptorpb.MethodOptions{Deprecated: scalar.Bool(true)},
+ },
+ },
+ },
+ },
+ }
+ checkAccessors(t, "", reflect.ValueOf(fd), want)
+}
+func checkAccessors(t *testing.T, p string, rv reflect.Value, want map[string]interface{}) {
+ p0 := p
+ defer func() {
+ if ex := recover(); ex != nil {
+ t.Errorf("panic at %v: %v", p, ex)
+ }
+ }()
+
+ if rv.Interface() == nil {
+ t.Errorf("%v is nil, want non-nil", p)
+ return
+ }
+ for s, v := range want {
+ // Call the accessor method.
+ p = p0 + "." + s
+ var rets []reflect.Value
+ if i := strings.IndexByte(s, ':'); i >= 0 {
+ // Accessor method takes in a single argument, which is encoded
+ // after the accessor name, separated by a ':' delimiter.
+ fnc := rv.MethodByName(s[:i])
+ arg := reflect.New(fnc.Type().In(0)).Elem()
+ s = s[i+len(":"):]
+ switch arg.Kind() {
+ case reflect.String:
+ arg.SetString(s)
+ case reflect.Int32, reflect.Int:
+ n, _ := strconv.ParseInt(s, 0, 64)
+ arg.SetInt(n)
+ }
+ rets = fnc.Call([]reflect.Value{arg})
+ } else {
+ rets = rv.MethodByName(s).Call(nil)
+ }
+
+ // Check that (val, ok) pattern is internally consistent.
+ if len(rets) == 2 {
+ if rets[0].IsNil() && rets[1].Bool() {
+ t.Errorf("%v = (nil, true), want (nil, false)", p)
+ }
+ if !rets[0].IsNil() && !rets[1].Bool() {
+ t.Errorf("%v = (non-nil, false), want (non-nil, true)", p)
+ }
+ }
+
+ // Check that the accessor output matches.
+ if want, ok := v.(map[string]interface{}); ok {
+ checkAccessors(t, p, rets[0], want)
+ continue
+ }
+
+ got := rets[0].Interface()
+ if pv, ok := got.(pref.Value); ok {
+ got = pv.Interface()
+ }
+
+ // Compare with proto.Equal if possible.
+ gotMsg, gotMsgOK := got.(proto.Message)
+ wantMsg, wantMsgOK := v.(proto.Message)
+ if gotMsgOK && wantMsgOK {
+ gotNil := reflect.ValueOf(gotMsg).IsNil()
+ wantNil := reflect.ValueOf(wantMsg).IsNil()
+ switch {
+ case !gotNil && wantNil:
+ t.Errorf("%v = non-nil, want nil", p)
+ case gotNil && !wantNil:
+ t.Errorf("%v = nil, want non-nil", p)
+ case !proto.Equal(gotMsg, wantMsg):
+ t.Errorf("%v = %v, want %v", p, gotMsg, wantMsg)
+ }
+ continue
+ }
+
+ if want := v; !reflect.DeepEqual(got, want) {
+ t.Errorf("%v = %T(%v), want %T(%v)", p, got, got, want, want)
+ }
+ }
+}
+
+func testFileFormat(t *testing.T, fd pref.FileDescriptor) {
+ const want = `FileDescriptor{
+ Syntax: proto2
+ Path: "path/to/file.proto"
+ Package: test
+ Messages: [{
+ Name: A
+ IsMapEntry: true
+ Fields: [{
+ Name: key
+ Number: 1
+ Cardinality: optional
+ Kind: string
+ JSONName: "key"
+ }, {
+ Name: value
+ Number: 2
+ Cardinality: optional
+ Kind: message
+ JSONName: "value"
+ Message: test.B
+ }]
+ }, {
+ Name: B
+ Fields: [{
+ Name: field_one
+ Number: 1
+ Cardinality: optional
+ Kind: string
+ JSONName: "fieldOne"
+ HasDefault: true
+ Default: "hello, \"world!\"\n"
+ Oneof: O1
+ }, {
+ Name: field_two
+ Number: 2
+ Cardinality: optional
+ Kind: enum
+ HasJSONName: true
+ JSONName: "Field2"
+ HasDefault: true
+ Default: 1
+ Oneof: O2
+ Enum: test.E1
+ }, {
+ Name: field_three
+ Number: 3
+ Cardinality: optional
+ Kind: message
+ JSONName: "fieldThree"
+ Oneof: O2
+ Message: test.C
+ }, {
+ Name: field_four
+ Number: 4
+ Cardinality: repeated
+ Kind: message
+ HasJSONName: true
+ JSONName: "Field4"
+ IsMap: true
+ MapKey: string
+ MapValue: test.B
+ }, {
+ Name: field_five
+ Number: 5
+ Cardinality: repeated
+ Kind: int32
+ JSONName: "fieldFive"
+ IsPacked: true
+ IsList: true
+ }, {
+ Name: field_six
+ Number: 6
+ Cardinality: required
+ Kind: bytes
+ JSONName: "fieldSix"
+ }]
+ Oneofs: [{
+ Name: O1
+ Fields: [field_one]
+ }, {
+ Name: O2
+ Fields: [field_two, field_three]
+ }]
+ ReservedNames: [fizz, buzz]
+ ReservedRanges: [100:200, 300]
+ RequiredNumbers: [6]
+ ExtensionRanges: [1000:2000, 3000]
+ }, {
+ Name: C
+ Messages: [{
+ Name: A
+ Fields: [{
+ Name: F
+ Number: 1
+ Cardinality: required
+ Kind: bytes
+ JSONName: "F"
+ HasDefault: true
+ Default: "dead\xbe\xef"
+ }]
+ RequiredNumbers: [1]
+ }]
+ Enums: [{
+ Name: E1
+ Values: [
+ {Name: FOO}
+ {Name: BAR, Number: 1}
+ ]
+ }]
+ Extensions: [{
+ Name: X
+ Number: 1000
+ Cardinality: repeated
+ Kind: message
+ JSONName: "X"
+ IsExtension: true
+ IsList: true
+ Extendee: test.B
+ Message: test.C
+ }]
+ }]
+ Enums: [{
+ Name: E1
+ Values: [
+ {Name: FOO}
+ {Name: BAR, Number: 1}
+ ]
+ ReservedNames: [FIZZ, BUZZ]
+ ReservedRanges: [10:20, 30]
+ }]
+ Extensions: [{
+ Name: X
+ Number: 1000
+ Cardinality: repeated
+ Kind: message
+ JSONName: "X"
+ IsPacked: true
+ IsExtension: true
+ IsList: true
+ Extendee: test.B
+ Message: test.C
+ }]
+ Services: [{
+ Name: S
+ Methods: [{
+ Name: M
+ Input: test.A
+ Output: test.C.A
+ IsStreamingClient: true
+ IsStreamingServer: true
+ }]
+ }]
+}`
+ tests := []struct{ fmt, want string }{{"%v", compactMultiFormat(want)}, {"%+v", want}}
+ for _, tt := range tests {
+ got := fmt.Sprintf(tt.fmt, fd)
+ if diff := cmp.Diff(got, tt.want); diff != "" {
+ t.Errorf("fmt.Sprintf(%q, fd) mismatch (-got +want):\n%s", tt.fmt, diff)
+ }
+ }
+}
+
+// compactMultiFormat returns the single line form of a multi line output.
+func compactMultiFormat(s string) string {
+ var b []byte
+ for _, s := range strings.Split(s, "\n") {
+ s = strings.TrimSpace(s)
+ s = regexp.MustCompile(": +").ReplaceAllString(s, ": ")
+ prevWord := len(b) > 0 && b[len(b)-1] != '[' && b[len(b)-1] != '{'
+ nextWord := len(s) > 0 && s[0] != ']' && s[0] != '}'
+ if prevWord && nextWord {
+ b = append(b, ", "...)
+ }
+ b = append(b, s...)
+ }
+ return string(b)
+}
diff --git a/internal/filedesc/name_pure.go b/internal/filedesc/name_pure.go
new file mode 100644
index 0000000..5626829
--- /dev/null
+++ b/internal/filedesc/name_pure.go
@@ -0,0 +1,34 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build purego appengine
+
+package filedesc
+
+import pref "google.golang.org/protobuf/reflect/protoreflect"
+
+func getNameBuilder() *nameBuilder { return nil }
+func putNameBuilder(*nameBuilder) {}
+
+type nameBuilder struct{}
+
+// MakeFullName converts b to a protoreflect.FullName,
+// where b must start with a leading dot.
+func (*nameBuilder) MakeFullName(b []byte) pref.FullName {
+ if len(b) == 0 || b[0] != '.' {
+ panic("name reference must be fully qualified")
+ }
+ return pref.FullName(b[1:])
+}
+
+// AppendFullName is equivalent to protoreflect.FullName.Append.
+func (*nameBuilder) AppendFullName(prefix pref.FullName, name []byte) pref.FullName {
+ return prefix.Append(pref.Name(name))
+}
+
+// MakeString is equivalent to string(b), but optimized for large batches
+// with a shared lifetime.
+func (*nameBuilder) MakeString(b []byte) string {
+ return string(b)
+}
diff --git a/internal/filedesc/name_unsafe.go b/internal/filedesc/name_unsafe.go
new file mode 100644
index 0000000..317114f
--- /dev/null
+++ b/internal/filedesc/name_unsafe.go
@@ -0,0 +1,124 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !purego,!appengine
+
+package filedesc
+
+import (
+ "sync"
+ "unsafe"
+
+ pref "google.golang.org/protobuf/reflect/protoreflect"
+)
+
+var nameBuilderPool = sync.Pool{
+ New: func() interface{} { return new(nameBuilder) },
+}
+
+func getNameBuilder() *nameBuilder {
+ return nameBuilderPool.Get().(*nameBuilder)
+}
+func putNameBuilder(b *nameBuilder) {
+ nameBuilderPool.Put(b)
+}
+
+type nameBuilder struct {
+ sb stringBuilder
+}
+
+// MakeFullName converts b to a protoreflect.FullName,
+// where b must start with a leading dot.
+func (nb *nameBuilder) MakeFullName(b []byte) pref.FullName {
+ if len(b) == 0 || b[0] != '.' {
+ panic("name reference must be fully qualified")
+ }
+ return pref.FullName(nb.MakeString(b[1:]))
+}
+
+// AppendFullName is equivalent to protoreflect.FullName.Append,
+// but optimized for large batches where each name has a shared lifetime.
+func (nb *nameBuilder) AppendFullName(prefix pref.FullName, name []byte) pref.FullName {
+ n := len(prefix) + len(".") + len(name)
+ if len(prefix) == 0 {
+ n -= len(".")
+ }
+ nb.grow(n)
+ nb.sb.WriteString(string(prefix))
+ nb.sb.WriteByte('.')
+ nb.sb.Write(name)
+ return pref.FullName(nb.last(n))
+}
+
+// MakeString is equivalent to string(b), but optimized for large batches
+// with a shared lifetime.
+func (nb *nameBuilder) MakeString(b []byte) string {
+ nb.grow(len(b))
+ nb.sb.Write(b)
+ return nb.last(len(b))
+}
+
+func (nb *nameBuilder) last(n int) string {
+ s := nb.sb.String()
+ return s[len(s)-n:]
+}
+
+func (nb *nameBuilder) grow(n int) {
+ const batchSize = 1 << 16
+ if nb.sb.Cap()-nb.sb.Len() < n {
+ nb.sb.Reset()
+ nb.sb.Grow(batchSize)
+ }
+}
+
+// stringsBuilder is a simplified copy of the strings.Builder from Go1.12:
+// * removed the shallow copy check
+// * removed methods that we do not use (e.g. WriteRune)
+//
+// A forked version is used:
+// * to enable Go1.9 support, but strings.Builder was added in Go1.10
+// * for the Cap method, which was missing until Go1.12
+//
+// TODO: Remove this when Go1.12 is the minimally supported toolchain version.
+type stringBuilder struct {
+ buf []byte
+}
+
+func (b *stringBuilder) String() string {
+ return *(*string)(unsafe.Pointer(&b.buf))
+}
+func (b *stringBuilder) Len() int {
+ return len(b.buf)
+}
+func (b *stringBuilder) Cap() int {
+ return cap(b.buf)
+}
+func (b *stringBuilder) Reset() {
+ b.buf = nil
+}
+func (b *stringBuilder) grow(n int) {
+ buf := make([]byte, len(b.buf), 2*cap(b.buf)+n)
+ copy(buf, b.buf)
+ b.buf = buf
+}
+func (b *stringBuilder) Grow(n int) {
+ if n < 0 {
+ panic("stringBuilder.Grow: negative count")
+ }
+ if cap(b.buf)-len(b.buf) < n {
+ b.grow(n)
+ }
+}
+func (b *stringBuilder) Write(p []byte) (int, error) {
+ b.buf = append(b.buf, p...)
+ return len(p), nil
+}
+func (b *stringBuilder) WriteByte(c byte) error {
+ b.buf = append(b.buf, c)
+ return nil
+}
+func (b *stringBuilder) WriteString(s string) (int, error) {
+ b.buf = append(b.buf, s...)
+ return len(s), nil
+}
diff --git a/internal/filedesc/placeholder.go b/internal/filedesc/placeholder.go
new file mode 100644
index 0000000..be95831
--- /dev/null
+++ b/internal/filedesc/placeholder.go
@@ -0,0 +1,90 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package filedesc
+
+import (
+ "google.golang.org/protobuf/internal/descopts"
+ "google.golang.org/protobuf/internal/pragma"
+ pref "google.golang.org/protobuf/reflect/protoreflect"
+)
+
+var (
+ emptyNames = new(Names)
+ emptyEnumRanges = new(EnumRanges)
+ emptyFieldRanges = new(FieldRanges)
+ emptyFieldNumbers = new(FieldNumbers)
+
+ emptyFiles = new(FileImports)
+ emptyMessages = new(Messages)
+ emptyFields = new(Fields)
+ emptyOneofs = new(Oneofs)
+ emptyEnums = new(Enums)
+ emptyEnumValues = new(EnumValues)
+ emptyExtensions = new(Extensions)
+ emptyServices = new(Services)
+)
+
+// PlaceholderFile is a placeholder, representing only the file path.
+type PlaceholderFile string
+
+func (f PlaceholderFile) ParentFile() pref.FileDescriptor { return f }
+func (f PlaceholderFile) Parent() pref.Descriptor { return nil }
+func (f PlaceholderFile) Index() int { return 0 }
+func (f PlaceholderFile) Syntax() pref.Syntax { return 0 }
+func (f PlaceholderFile) Name() pref.Name { return "" }
+func (f PlaceholderFile) FullName() pref.FullName { return "" }
+func (f PlaceholderFile) IsPlaceholder() bool { return true }
+func (f PlaceholderFile) Options() pref.ProtoMessage { return descopts.File }
+func (f PlaceholderFile) Path() string { return string(f) }
+func (f PlaceholderFile) Package() pref.FullName { return "" }
+func (f PlaceholderFile) Imports() pref.FileImports { return emptyFiles }
+func (f PlaceholderFile) Messages() pref.MessageDescriptors { return emptyMessages }
+func (f PlaceholderFile) Enums() pref.EnumDescriptors { return emptyEnums }
+func (f PlaceholderFile) Extensions() pref.ExtensionDescriptors { return emptyExtensions }
+func (f PlaceholderFile) Services() pref.ServiceDescriptors { return emptyServices }
+func (f PlaceholderFile) ProtoType(pref.FileDescriptor) { return }
+func (f PlaceholderFile) ProtoInternal(pragma.DoNotImplement) { return }
+
+// PlaceholderEnum is a placeholder, representing only the full name.
+type PlaceholderEnum pref.FullName
+
+func (e PlaceholderEnum) ParentFile() pref.FileDescriptor { return nil }
+func (e PlaceholderEnum) Parent() pref.Descriptor { return nil }
+func (e PlaceholderEnum) Index() int { return 0 }
+func (e PlaceholderEnum) Syntax() pref.Syntax { return 0 }
+func (e PlaceholderEnum) Name() pref.Name { return pref.FullName(e).Name() }
+func (e PlaceholderEnum) FullName() pref.FullName { return pref.FullName(e) }
+func (e PlaceholderEnum) IsPlaceholder() bool { return true }
+func (e PlaceholderEnum) Options() pref.ProtoMessage { return descopts.Enum }
+func (e PlaceholderEnum) Values() pref.EnumValueDescriptors { return emptyEnumValues }
+func (e PlaceholderEnum) ReservedNames() pref.Names { return emptyNames }
+func (e PlaceholderEnum) ReservedRanges() pref.EnumRanges { return emptyEnumRanges }
+func (e PlaceholderEnum) ProtoType(pref.EnumDescriptor) { return }
+func (e PlaceholderEnum) ProtoInternal(pragma.DoNotImplement) { return }
+
+// PlaceholderMessage is a placeholder, representing only the full name.
+type PlaceholderMessage pref.FullName
+
+func (m PlaceholderMessage) ParentFile() pref.FileDescriptor { return nil }
+func (m PlaceholderMessage) Parent() pref.Descriptor { return nil }
+func (m PlaceholderMessage) Index() int { return 0 }
+func (m PlaceholderMessage) Syntax() pref.Syntax { return 0 }
+func (m PlaceholderMessage) Name() pref.Name { return pref.FullName(m).Name() }
+func (m PlaceholderMessage) FullName() pref.FullName { return pref.FullName(m) }
+func (m PlaceholderMessage) IsPlaceholder() bool { return true }
+func (m PlaceholderMessage) Options() pref.ProtoMessage { return descopts.Message }
+func (m PlaceholderMessage) IsMapEntry() bool { return false }
+func (m PlaceholderMessage) Fields() pref.FieldDescriptors { return emptyFields }
+func (m PlaceholderMessage) Oneofs() pref.OneofDescriptors { return emptyOneofs }
+func (m PlaceholderMessage) ReservedNames() pref.Names { return emptyNames }
+func (m PlaceholderMessage) ReservedRanges() pref.FieldRanges { return emptyFieldRanges }
+func (m PlaceholderMessage) RequiredNumbers() pref.FieldNumbers { return emptyFieldNumbers }
+func (m PlaceholderMessage) ExtensionRanges() pref.FieldRanges { return emptyFieldRanges }
+func (m PlaceholderMessage) ExtensionRangeOptions(int) pref.ProtoMessage { panic("index out of range") }
+func (m PlaceholderMessage) Messages() pref.MessageDescriptors { return emptyMessages }
+func (m PlaceholderMessage) Enums() pref.EnumDescriptors { return emptyEnums }
+func (m PlaceholderMessage) Extensions() pref.ExtensionDescriptors { return emptyExtensions }
+func (m PlaceholderMessage) ProtoType(pref.MessageDescriptor) { return }
+func (m PlaceholderMessage) ProtoInternal(pragma.DoNotImplement) { return }