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