256
|
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 filetype provides functionality for wrapping descriptors |
|
6 |
// with Go type information. |
|
7 |
package filetype |
|
8 |
|
|
9 |
import ( |
|
10 |
"reflect" |
|
11 |
|
|
12 |
"google.golang.org/protobuf/internal/descopts" |
260
|
13 |
"google.golang.org/protobuf/internal/filedesc" |
256
|
14 |
pimpl "google.golang.org/protobuf/internal/impl" |
260
|
15 |
"google.golang.org/protobuf/reflect/protoreflect" |
|
16 |
"google.golang.org/protobuf/reflect/protoregistry" |
256
|
17 |
) |
|
18 |
|
|
19 |
// Builder constructs type descriptors from a raw file descriptor |
|
20 |
// and associated Go types for each enum and message declaration. |
|
21 |
// |
260
|
22 |
// # Flattened Ordering |
256
|
23 |
// |
|
24 |
// The protobuf type system represents declarations as a tree. Certain nodes in |
|
25 |
// the tree require us to either associate it with a concrete Go type or to |
|
26 |
// resolve a dependency, which is information that must be provided separately |
|
27 |
// since it cannot be derived from the file descriptor alone. |
|
28 |
// |
|
29 |
// However, representing a tree as Go literals is difficult to simply do in a |
|
30 |
// space and time efficient way. Thus, we store them as a flattened list of |
|
31 |
// objects where the serialization order from the tree-based form is important. |
|
32 |
// |
|
33 |
// The "flattened ordering" is defined as a tree traversal of all enum, message, |
|
34 |
// extension, and service declarations using the following algorithm: |
|
35 |
// |
|
36 |
// def VisitFileDecls(fd): |
|
37 |
// for e in fd.Enums: yield e |
|
38 |
// for m in fd.Messages: yield m |
|
39 |
// for x in fd.Extensions: yield x |
|
40 |
// for s in fd.Services: yield s |
|
41 |
// for m in fd.Messages: yield from VisitMessageDecls(m) |
|
42 |
// |
|
43 |
// def VisitMessageDecls(md): |
|
44 |
// for e in md.Enums: yield e |
|
45 |
// for m in md.Messages: yield m |
|
46 |
// for x in md.Extensions: yield x |
|
47 |
// for m in md.Messages: yield from VisitMessageDecls(m) |
|
48 |
// |
|
49 |
// The traversal starts at the root file descriptor and yields each direct |
|
50 |
// declaration within each node before traversing into sub-declarations |
|
51 |
// that children themselves may have. |
|
52 |
type Builder struct { |
|
53 |
// File is the underlying file descriptor builder. |
260
|
54 |
File filedesc.Builder |
256
|
55 |
|
|
56 |
// GoTypes is a unique set of the Go types for all declarations and |
|
57 |
// dependencies. Each type is represented as a zero value of the Go type. |
|
58 |
// |
|
59 |
// Declarations are Go types generated for enums and messages directly |
|
60 |
// declared (not publicly imported) in the proto source file. |
|
61 |
// Messages for map entries are accounted for, but represented by nil. |
|
62 |
// Enum declarations in "flattened ordering" come first, followed by |
|
63 |
// message declarations in "flattened ordering". |
|
64 |
// |
|
65 |
// Dependencies are Go types for enums or messages referenced by |
|
66 |
// message fields (excluding weak fields), for parent extended messages of |
|
67 |
// extension fields, for enums or messages referenced by extension fields, |
|
68 |
// and for input and output messages referenced by service methods. |
|
69 |
// Dependencies must come after declarations, but the ordering of |
|
70 |
// dependencies themselves is unspecified. |
|
71 |
GoTypes []interface{} |
|
72 |
|
|
73 |
// DependencyIndexes is an ordered list of indexes into GoTypes for the |
|
74 |
// dependencies of messages, extensions, or services. |
|
75 |
// |
|
76 |
// There are 5 sub-lists in "flattened ordering" concatenated back-to-back: |
|
77 |
// 0. Message field dependencies: list of the enum or message type |
|
78 |
// referred to by every message field. |
|
79 |
// 1. Extension field targets: list of the extended parent message of |
|
80 |
// every extension. |
|
81 |
// 2. Extension field dependencies: list of the enum or message type |
|
82 |
// referred to by every extension field. |
|
83 |
// 3. Service method inputs: list of the input message type |
|
84 |
// referred to by every service method. |
|
85 |
// 4. Service method outputs: list of the output message type |
|
86 |
// referred to by every service method. |
|
87 |
// |
|
88 |
// The offset into DependencyIndexes for the start of each sub-list |
|
89 |
// is appended to the end in reverse order. |
|
90 |
DependencyIndexes []int32 |
|
91 |
|
|
92 |
// EnumInfos is a list of enum infos in "flattened ordering". |
|
93 |
EnumInfos []pimpl.EnumInfo |
|
94 |
|
|
95 |
// MessageInfos is a list of message infos in "flattened ordering". |
|
96 |
// If provided, the GoType and PBType for each element is populated. |
|
97 |
// |
|
98 |
// Requirement: len(MessageInfos) == len(Build.Messages) |
|
99 |
MessageInfos []pimpl.MessageInfo |
|
100 |
|
|
101 |
// ExtensionInfos is a list of extension infos in "flattened ordering". |
|
102 |
// Each element is initialized and registered with the protoregistry package. |
|
103 |
// |
|
104 |
// Requirement: len(LegacyExtensions) == len(Build.Extensions) |
|
105 |
ExtensionInfos []pimpl.ExtensionInfo |
|
106 |
|
|
107 |
// TypeRegistry is the registry to register each type descriptor. |
|
108 |
// If nil, it uses protoregistry.GlobalTypes. |
|
109 |
TypeRegistry interface { |
260
|
110 |
RegisterMessage(protoreflect.MessageType) error |
|
111 |
RegisterEnum(protoreflect.EnumType) error |
|
112 |
RegisterExtension(protoreflect.ExtensionType) error |
256
|
113 |
} |
|
114 |
} |
|
115 |
|
|
116 |
// Out is the output of the builder. |
|
117 |
type Out struct { |
260
|
118 |
File protoreflect.FileDescriptor |
256
|
119 |
} |
|
120 |
|
|
121 |
func (tb Builder) Build() (out Out) { |
|
122 |
// Replace the resolver with one that resolves dependencies by index, |
|
123 |
// which is faster and more reliable than relying on the global registry. |
|
124 |
if tb.File.FileRegistry == nil { |
260
|
125 |
tb.File.FileRegistry = protoregistry.GlobalFiles |
256
|
126 |
} |
|
127 |
tb.File.FileRegistry = &resolverByIndex{ |
|
128 |
goTypes: tb.GoTypes, |
|
129 |
depIdxs: tb.DependencyIndexes, |
|
130 |
fileRegistry: tb.File.FileRegistry, |
|
131 |
} |
|
132 |
|
|
133 |
// Initialize registry if unpopulated. |
|
134 |
if tb.TypeRegistry == nil { |
260
|
135 |
tb.TypeRegistry = protoregistry.GlobalTypes |
256
|
136 |
} |
|
137 |
|
|
138 |
fbOut := tb.File.Build() |
|
139 |
out.File = fbOut.File |
|
140 |
|
|
141 |
// Process enums. |
|
142 |
enumGoTypes := tb.GoTypes[:len(fbOut.Enums)] |
|
143 |
if len(tb.EnumInfos) != len(fbOut.Enums) { |
|
144 |
panic("mismatching enum lengths") |
|
145 |
} |
|
146 |
if len(fbOut.Enums) > 0 { |
|
147 |
for i := range fbOut.Enums { |
|
148 |
tb.EnumInfos[i] = pimpl.EnumInfo{ |
|
149 |
GoReflectType: reflect.TypeOf(enumGoTypes[i]), |
|
150 |
Desc: &fbOut.Enums[i], |
|
151 |
} |
|
152 |
// Register enum types. |
|
153 |
if err := tb.TypeRegistry.RegisterEnum(&tb.EnumInfos[i]); err != nil { |
|
154 |
panic(err) |
|
155 |
} |
|
156 |
} |
|
157 |
} |
|
158 |
|
|
159 |
// Process messages. |
|
160 |
messageGoTypes := tb.GoTypes[len(fbOut.Enums):][:len(fbOut.Messages)] |
|
161 |
if len(tb.MessageInfos) != len(fbOut.Messages) { |
|
162 |
panic("mismatching message lengths") |
|
163 |
} |
|
164 |
if len(fbOut.Messages) > 0 { |
|
165 |
for i := range fbOut.Messages { |
|
166 |
if messageGoTypes[i] == nil { |
|
167 |
continue // skip map entry |
|
168 |
} |
|
169 |
|
|
170 |
tb.MessageInfos[i].GoReflectType = reflect.TypeOf(messageGoTypes[i]) |
|
171 |
tb.MessageInfos[i].Desc = &fbOut.Messages[i] |
|
172 |
|
|
173 |
// Register message types. |
|
174 |
if err := tb.TypeRegistry.RegisterMessage(&tb.MessageInfos[i]); err != nil { |
|
175 |
panic(err) |
|
176 |
} |
|
177 |
} |
|
178 |
|
|
179 |
// As a special-case for descriptor.proto, |
|
180 |
// locally register concrete message type for the options. |
|
181 |
if out.File.Path() == "google/protobuf/descriptor.proto" && out.File.Package() == "google.protobuf" { |
|
182 |
for i := range fbOut.Messages { |
|
183 |
switch fbOut.Messages[i].Name() { |
|
184 |
case "FileOptions": |
260
|
185 |
descopts.File = messageGoTypes[i].(protoreflect.ProtoMessage) |
256
|
186 |
case "EnumOptions": |
260
|
187 |
descopts.Enum = messageGoTypes[i].(protoreflect.ProtoMessage) |
256
|
188 |
case "EnumValueOptions": |
260
|
189 |
descopts.EnumValue = messageGoTypes[i].(protoreflect.ProtoMessage) |
256
|
190 |
case "MessageOptions": |
260
|
191 |
descopts.Message = messageGoTypes[i].(protoreflect.ProtoMessage) |
256
|
192 |
case "FieldOptions": |
260
|
193 |
descopts.Field = messageGoTypes[i].(protoreflect.ProtoMessage) |
256
|
194 |
case "OneofOptions": |
260
|
195 |
descopts.Oneof = messageGoTypes[i].(protoreflect.ProtoMessage) |
256
|
196 |
case "ExtensionRangeOptions": |
260
|
197 |
descopts.ExtensionRange = messageGoTypes[i].(protoreflect.ProtoMessage) |
256
|
198 |
case "ServiceOptions": |
260
|
199 |
descopts.Service = messageGoTypes[i].(protoreflect.ProtoMessage) |
256
|
200 |
case "MethodOptions": |
260
|
201 |
descopts.Method = messageGoTypes[i].(protoreflect.ProtoMessage) |
256
|
202 |
} |
|
203 |
} |
|
204 |
} |
|
205 |
} |
|
206 |
|
|
207 |
// Process extensions. |
|
208 |
if len(tb.ExtensionInfos) != len(fbOut.Extensions) { |
|
209 |
panic("mismatching extension lengths") |
|
210 |
} |
|
211 |
var depIdx int32 |
|
212 |
for i := range fbOut.Extensions { |
|
213 |
// For enum and message kinds, determine the referent Go type so |
|
214 |
// that we can construct their constructors. |
|
215 |
const listExtDeps = 2 |
|
216 |
var goType reflect.Type |
|
217 |
switch fbOut.Extensions[i].L1.Kind { |
260
|
218 |
case protoreflect.EnumKind: |
256
|
219 |
j := depIdxs.Get(tb.DependencyIndexes, listExtDeps, depIdx) |
|
220 |
goType = reflect.TypeOf(tb.GoTypes[j]) |
|
221 |
depIdx++ |
260
|
222 |
case protoreflect.MessageKind, protoreflect.GroupKind: |
256
|
223 |
j := depIdxs.Get(tb.DependencyIndexes, listExtDeps, depIdx) |
|
224 |
goType = reflect.TypeOf(tb.GoTypes[j]) |
|
225 |
depIdx++ |
|
226 |
default: |
|
227 |
goType = goTypeForPBKind[fbOut.Extensions[i].L1.Kind] |
|
228 |
} |
|
229 |
if fbOut.Extensions[i].IsList() { |
|
230 |
goType = reflect.SliceOf(goType) |
|
231 |
} |
|
232 |
|
|
233 |
pimpl.InitExtensionInfo(&tb.ExtensionInfos[i], &fbOut.Extensions[i], goType) |
|
234 |
|
|
235 |
// Register extension types. |
|
236 |
if err := tb.TypeRegistry.RegisterExtension(&tb.ExtensionInfos[i]); err != nil { |
|
237 |
panic(err) |
|
238 |
} |
|
239 |
} |
|
240 |
|
|
241 |
return out |
|
242 |
} |
|
243 |
|
260
|
244 |
var goTypeForPBKind = map[protoreflect.Kind]reflect.Type{ |
|
245 |
protoreflect.BoolKind: reflect.TypeOf(bool(false)), |
|
246 |
protoreflect.Int32Kind: reflect.TypeOf(int32(0)), |
|
247 |
protoreflect.Sint32Kind: reflect.TypeOf(int32(0)), |
|
248 |
protoreflect.Sfixed32Kind: reflect.TypeOf(int32(0)), |
|
249 |
protoreflect.Int64Kind: reflect.TypeOf(int64(0)), |
|
250 |
protoreflect.Sint64Kind: reflect.TypeOf(int64(0)), |
|
251 |
protoreflect.Sfixed64Kind: reflect.TypeOf(int64(0)), |
|
252 |
protoreflect.Uint32Kind: reflect.TypeOf(uint32(0)), |
|
253 |
protoreflect.Fixed32Kind: reflect.TypeOf(uint32(0)), |
|
254 |
protoreflect.Uint64Kind: reflect.TypeOf(uint64(0)), |
|
255 |
protoreflect.Fixed64Kind: reflect.TypeOf(uint64(0)), |
|
256 |
protoreflect.FloatKind: reflect.TypeOf(float32(0)), |
|
257 |
protoreflect.DoubleKind: reflect.TypeOf(float64(0)), |
|
258 |
protoreflect.StringKind: reflect.TypeOf(string("")), |
|
259 |
protoreflect.BytesKind: reflect.TypeOf([]byte(nil)), |
256
|
260 |
} |
|
261 |
|
|
262 |
type depIdxs []int32 |
|
263 |
|
|
264 |
// Get retrieves the jth element of the ith sub-list. |
|
265 |
func (x depIdxs) Get(i, j int32) int32 { |
|
266 |
return x[x[int32(len(x))-i-1]+j] |
|
267 |
} |
|
268 |
|
|
269 |
type ( |
|
270 |
resolverByIndex struct { |
|
271 |
goTypes []interface{} |
|
272 |
depIdxs depIdxs |
|
273 |
fileRegistry |
|
274 |
} |
|
275 |
fileRegistry interface { |
260
|
276 |
FindFileByPath(string) (protoreflect.FileDescriptor, error) |
|
277 |
FindDescriptorByName(protoreflect.FullName) (protoreflect.Descriptor, error) |
|
278 |
RegisterFile(protoreflect.FileDescriptor) error |
256
|
279 |
} |
|
280 |
) |
|
281 |
|
260
|
282 |
func (r *resolverByIndex) FindEnumByIndex(i, j int32, es []filedesc.Enum, ms []filedesc.Message) protoreflect.EnumDescriptor { |
256
|
283 |
if depIdx := int(r.depIdxs.Get(i, j)); int(depIdx) < len(es)+len(ms) { |
|
284 |
return &es[depIdx] |
|
285 |
} else { |
|
286 |
return pimpl.Export{}.EnumDescriptorOf(r.goTypes[depIdx]) |
|
287 |
} |
|
288 |
} |
|
289 |
|
260
|
290 |
func (r *resolverByIndex) FindMessageByIndex(i, j int32, es []filedesc.Enum, ms []filedesc.Message) protoreflect.MessageDescriptor { |
256
|
291 |
if depIdx := int(r.depIdxs.Get(i, j)); depIdx < len(es)+len(ms) { |
|
292 |
return &ms[depIdx-len(es)] |
|
293 |
} else { |
|
294 |
return pimpl.Export{}.MessageDescriptorOf(r.goTypes[depIdx]) |
|
295 |
} |
|
296 |
} |