build.go (5169B)
1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package filedesc provides functionality for constructing descriptors. 6 // 7 // The types in this package implement interfaces in the protoreflect package 8 // related to protobuf descripriptors. 9 package filedesc 10 11 import ( 12 "google.golang.org/protobuf/encoding/protowire" 13 "google.golang.org/protobuf/internal/genid" 14 "google.golang.org/protobuf/reflect/protoreflect" 15 "google.golang.org/protobuf/reflect/protoregistry" 16 ) 17 18 // Builder construct a protoreflect.FileDescriptor from the raw descriptor. 19 type Builder struct { 20 // GoPackagePath is the Go package path that is invoking this builder. 21 GoPackagePath string 22 23 // RawDescriptor is the wire-encoded bytes of FileDescriptorProto 24 // and must be populated. 25 RawDescriptor []byte 26 27 // NumEnums is the total number of enums declared in the file. 28 NumEnums int32 29 // NumMessages is the total number of messages declared in the file. 30 // It includes the implicit message declarations for map entries. 31 NumMessages int32 32 // NumExtensions is the total number of extensions declared in the file. 33 NumExtensions int32 34 // NumServices is the total number of services declared in the file. 35 NumServices int32 36 37 // TypeResolver resolves extension field types for descriptor options. 38 // If nil, it uses protoregistry.GlobalTypes. 39 TypeResolver interface { 40 protoregistry.ExtensionTypeResolver 41 } 42 43 // FileRegistry is use to lookup file, enum, and message dependencies. 44 // Once constructed, the file descriptor is registered here. 45 // If nil, it uses protoregistry.GlobalFiles. 46 FileRegistry interface { 47 FindFileByPath(string) (protoreflect.FileDescriptor, error) 48 FindDescriptorByName(protoreflect.FullName) (protoreflect.Descriptor, error) 49 RegisterFile(protoreflect.FileDescriptor) error 50 } 51 } 52 53 // resolverByIndex is an interface Builder.FileRegistry may implement. 54 // If so, it permits looking up an enum or message dependency based on the 55 // sub-list and element index into filetype.Builder.DependencyIndexes. 56 type resolverByIndex interface { 57 FindEnumByIndex(int32, int32, []Enum, []Message) protoreflect.EnumDescriptor 58 FindMessageByIndex(int32, int32, []Enum, []Message) protoreflect.MessageDescriptor 59 } 60 61 // Indexes of each sub-list in filetype.Builder.DependencyIndexes. 62 const ( 63 listFieldDeps int32 = iota 64 listExtTargets 65 listExtDeps 66 listMethInDeps 67 listMethOutDeps 68 ) 69 70 // Out is the output of the Builder. 71 type Out struct { 72 File protoreflect.FileDescriptor 73 74 // Enums is all enum descriptors in "flattened ordering". 75 Enums []Enum 76 // Messages is all message descriptors in "flattened ordering". 77 // It includes the implicit message declarations for map entries. 78 Messages []Message 79 // Extensions is all extension descriptors in "flattened ordering". 80 Extensions []Extension 81 // Service is all service descriptors in "flattened ordering". 82 Services []Service 83 } 84 85 // Build constructs a FileDescriptor given the parameters set in Builder. 86 // It assumes that the inputs are well-formed and panics if any inconsistencies 87 // are encountered. 88 // 89 // If NumEnums+NumMessages+NumExtensions+NumServices is zero, 90 // then Build automatically derives them from the raw descriptor. 91 func (db Builder) Build() (out Out) { 92 // Populate the counts if uninitialized. 93 if db.NumEnums+db.NumMessages+db.NumExtensions+db.NumServices == 0 { 94 db.unmarshalCounts(db.RawDescriptor, true) 95 } 96 97 // Initialize resolvers and registries if unpopulated. 98 if db.TypeResolver == nil { 99 db.TypeResolver = protoregistry.GlobalTypes 100 } 101 if db.FileRegistry == nil { 102 db.FileRegistry = protoregistry.GlobalFiles 103 } 104 105 fd := newRawFile(db) 106 out.File = fd 107 out.Enums = fd.allEnums 108 out.Messages = fd.allMessages 109 out.Extensions = fd.allExtensions 110 out.Services = fd.allServices 111 112 if err := db.FileRegistry.RegisterFile(fd); err != nil { 113 panic(err) 114 } 115 return out 116 } 117 118 // unmarshalCounts counts the number of enum, message, extension, and service 119 // declarations in the raw message, which is either a FileDescriptorProto 120 // or a MessageDescriptorProto depending on whether isFile is set. 121 func (db *Builder) unmarshalCounts(b []byte, isFile bool) { 122 for len(b) > 0 { 123 num, typ, n := protowire.ConsumeTag(b) 124 b = b[n:] 125 switch typ { 126 case protowire.BytesType: 127 v, m := protowire.ConsumeBytes(b) 128 b = b[m:] 129 if isFile { 130 switch num { 131 case genid.FileDescriptorProto_EnumType_field_number: 132 db.NumEnums++ 133 case genid.FileDescriptorProto_MessageType_field_number: 134 db.unmarshalCounts(v, false) 135 db.NumMessages++ 136 case genid.FileDescriptorProto_Extension_field_number: 137 db.NumExtensions++ 138 case genid.FileDescriptorProto_Service_field_number: 139 db.NumServices++ 140 } 141 } else { 142 switch num { 143 case genid.DescriptorProto_EnumType_field_number: 144 db.NumEnums++ 145 case genid.DescriptorProto_NestedType_field_number: 146 db.unmarshalCounts(v, false) 147 db.NumMessages++ 148 case genid.DescriptorProto_Extension_field_number: 149 db.NumExtensions++ 150 } 151 } 152 default: 153 m := protowire.ConsumeFieldValue(num, typ, b) 154 b = b[m:] 155 } 156 } 157 }