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