blob: b293b6947361d084a79be85040ee85efbe513030 [file] [log] [blame]
khenaidood948f772021-08-11 17:49:24 -04001// 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}