blob: 550cae1782458b1523412155a6fc92ea13e184f6 [file] [log] [blame]
Damien Neil8012b442019-01-18 09:32:24 -08001// Copyright 2018 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 fileinit constructs protoreflect.FileDescriptors from the encoded
6// file descriptor proto messages. This package uses a custom proto unmarshaler
7// 1) to avoid a dependency on the descriptor proto 2) for performance to keep
8// the initialization cost as low as possible.
9package fileinit
10
11import (
12 "fmt"
13 "reflect"
14 "sync"
15
Joe Tsaiafb455e2019-03-14 16:08:22 -070016 papi "github.com/golang/protobuf/protoapi"
Damien Neil8012b442019-01-18 09:32:24 -080017 pragma "github.com/golang/protobuf/v2/internal/pragma"
Joe Tsai990b9f52019-03-13 12:56:39 -070018 ptype "github.com/golang/protobuf/v2/internal/prototype"
Damien Neil8012b442019-01-18 09:32:24 -080019 pfmt "github.com/golang/protobuf/v2/internal/typefmt"
20 "github.com/golang/protobuf/v2/proto"
21 pref "github.com/golang/protobuf/v2/reflect/protoreflect"
Damien Neil8012b442019-01-18 09:32:24 -080022)
23
24// FileBuilder construct a protoreflect.FileDescriptor from the
25// raw file descriptor and the Go types for declarations and dependencies.
26//
27//
28// Flattened Ordering
29//
30// The protobuf type system represents declarations as a tree. Certain nodes in
31// the tree require us to either associate it with a concrete Go type or to
32// resolve a dependency, which is information that must be provided separately
33// since it cannot be derived from the file descriptor alone.
34//
35// However, representing a tree as Go literals is difficult to simply do in a
36// space and time efficient way. Thus, we store them as a flattened list of
37// objects where the serialization order from the tree-based form is important.
38//
39// The "flattened ordering" is defined as a tree traversal of all enum, message,
40// extension, and service declarations using the following algorithm:
41//
42// def VisitFileDecls(fd):
43// for e in fd.Enums: yield e
44// for m in fd.Messages: yield m
45// for x in fd.Extensions: yield x
46// for s in fd.Services: yield s
47// for m in fd.Messages: yield from VisitMessageDecls(m)
48//
49// def VisitMessageDecls(md):
50// for e in md.Enums: yield e
51// for m in md.Messages: yield m
52// for x in md.Extensions: yield x
53// for m in md.Messages: yield from VisitMessageDecls(m)
54//
55// The traversal starts at the root file descriptor and yields each direct
56// declaration within each node before traversing into sub-declarations
57// that children themselves may have.
58type FileBuilder struct {
59 // RawDescriptor is the wire-encoded bytes of FileDescriptorProto.
60 RawDescriptor []byte
61
62 // GoTypes is a unique set of the Go types for all declarations and
63 // dependencies. Each type is represented as a zero value of the Go type.
64 //
65 // Declarations are Go types generated for enums and messages directly
66 // declared (not publicly imported) in the proto source file.
67 // Messages for map entries are included, but represented by nil.
68 // Enum declarations in "flattened ordering" come first, followed by
69 // message declarations in "flattened ordering". The length of each sub-list
70 // is len(EnumOutputTypes) and len(MessageOutputTypes), respectively.
71 //
72 // Dependencies are Go types for enums or messages referenced by
73 // message fields (excluding weak fields), for parent extended messages of
74 // extension fields, for enums or messages referenced by extension fields,
75 // and for input and output messages referenced by service methods.
76 // Dependencies must come after declarations, but the ordering of
77 // dependencies themselves is unspecified.
78 GoTypes []interface{}
79
80 // DependencyIndexes is an ordered list of indexes into GoTypes for the
81 // dependencies of messages, extensions, or services. There are 4 sub-lists
82 // each in "flattened ordering" concatenated back-to-back:
83 // * Extension field targets: list of the extended parent message of
84 // every extension. Length is len(ExtensionOutputTypes).
85 // * Message field dependencies: list of the enum or message type
86 // referred to by every message field.
87 // * Extension field dependencies: list of the enum or message type
88 // referred to by every extension field.
89 // * Service method dependencies: list of the input and output message type
90 // referred to by every service method.
91 DependencyIndexes []int32
92
93 // TODO: Provide a list of imported files.
94 // FileDependencies []pref.FileDescriptor
95
96 // TODO: Provide a list of extension types for options extensions.
97 // OptionDependencies []pref.ExtensionType
98
Joe Tsaiafb455e2019-03-14 16:08:22 -070099 // LegacyExtensions are a list of legacy extension descriptors.
100 // If provided, the pointer to the v1 ExtensionDesc will be stored into the
101 // associated v2 ExtensionType and accessible via a pseudo-internal API.
102 // Also, the v2 ExtensionType will be stored into each v1 ExtensionDesc.
103 // If non-nil, len(LegacyExtensions) must equal len(ExtensionOutputTypes).
104 LegacyExtensions []papi.ExtensionDesc
105
Damien Neil8012b442019-01-18 09:32:24 -0800106 // EnumOutputTypes is where Init stores all initialized enum types
107 // in "flattened ordering".
108 EnumOutputTypes []pref.EnumType
109 // MessageOutputTypes is where Init stores all initialized message types
110 // in "flattened ordering"; this includes map entry types.
111 MessageOutputTypes []pref.MessageType
112 // ExtensionOutputTypes is where Init stores all initialized extension types
113 // in "flattened ordering".
114 ExtensionOutputTypes []pref.ExtensionType
115
116 // TODO: Provide ability for FileBuilder to handle registration?
117 // FilesRegistry *pref.Files
118 // TypesRegistry *pref.Types
119}
120
121// Init constructs a FileDescriptor given the parameters set in FileBuilder.
122// It assumes that the inputs are well-formed and panics if any inconsistencies
123// are encountered.
124func (fb FileBuilder) Init() pref.FileDescriptor {
125 fd := newFileDesc(fb)
126
Joe Tsaiafb455e2019-03-14 16:08:22 -0700127 if fb.LegacyExtensions != nil {
128 for i := range fd.allExtensions {
129 fd.allExtensions[i].legacyDesc = &fb.LegacyExtensions[i]
130 fb.LegacyExtensions[i].Type = &fd.allExtensions[i]
131 }
132 }
133
Damien Neil8012b442019-01-18 09:32:24 -0800134 for i := range fd.allEnums {
135 fb.EnumOutputTypes[i] = &fd.allEnums[i]
136 }
137 for i := range fd.allMessages {
138 fb.MessageOutputTypes[i] = &fd.allMessages[i]
139 }
140 for i := range fd.allExtensions {
141 fb.ExtensionOutputTypes[i] = &fd.allExtensions[i]
142 }
143 return fd
144}
145
146type (
147 // fileInit contains a copy of certain fields in FileBuilder for use during
148 // lazy initialization upon first use.
149 fileInit struct {
150 RawDescriptor []byte
151 GoTypes []interface{}
152 DependencyIndexes []int32
153 }
154 fileDesc struct {
155 fileInit
156
157 path string
158 protoPackage pref.FullName
159
160 fileDecls
161
162 enums enumDescs
163 messages messageDescs
164 extensions extensionDescs
165 services serviceDescs
166
167 once sync.Once
168 lazy *fileLazy // protected by once
169 }
170 fileDecls struct {
171 allEnums []enumDesc
172 allMessages []messageDesc
173 allExtensions []extensionDesc
174 }
175 fileLazy struct {
176 syntax pref.Syntax
177 imports fileImports
178 byName map[pref.FullName]pref.Descriptor
179 options []byte
180 }
181)
182
183func (fd *fileDesc) Parent() (pref.Descriptor, bool) { return nil, false }
184func (fd *fileDesc) Index() int { return 0 }
185func (fd *fileDesc) Syntax() pref.Syntax { return fd.lazyInit().syntax }
186func (fd *fileDesc) Name() pref.Name { return fd.Package().Name() }
187func (fd *fileDesc) FullName() pref.FullName { return fd.Package() }
188func (fd *fileDesc) IsPlaceholder() bool { return false }
189func (fd *fileDesc) Options() pref.OptionsMessage {
190 return unmarshalOptions(ptype.X.FileOptions(), fd.lazyInit().options)
191}
192func (fd *fileDesc) Path() string { return fd.path }
193func (fd *fileDesc) Package() pref.FullName { return fd.protoPackage }
194func (fd *fileDesc) Imports() pref.FileImports { return &fd.lazyInit().imports }
195func (fd *fileDesc) Enums() pref.EnumDescriptors { return &fd.enums }
196func (fd *fileDesc) Messages() pref.MessageDescriptors { return &fd.messages }
197func (fd *fileDesc) Extensions() pref.ExtensionDescriptors { return &fd.extensions }
198func (fd *fileDesc) Services() pref.ServiceDescriptors { return &fd.services }
199func (fd *fileDesc) DescriptorByName(s pref.FullName) pref.Descriptor { return fd.lazyInit().byName[s] }
200func (fd *fileDesc) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, fd) }
201func (fd *fileDesc) ProtoType(pref.FileDescriptor) {}
202func (fd *fileDesc) ProtoInternal(pragma.DoNotImplement) {}
203
204type (
205 enumDesc struct {
206 baseDesc
207
208 lazy *enumLazy // protected by fileDesc.once
209 }
210 enumLazy struct {
211 typ reflect.Type
212 new func(pref.EnumNumber) pref.Enum
213
214 values enumValueDescs
215 resvNames names
216 resvRanges enumRanges
217 options []byte
218 }
219 enumValueDesc struct {
220 baseDesc
221
222 number pref.EnumNumber
223 options []byte
224 }
225)
226
227func (ed *enumDesc) GoType() reflect.Type { return ed.lazyInit().typ }
228func (ed *enumDesc) New(n pref.EnumNumber) pref.Enum { return ed.lazyInit().new(n) }
229func (ed *enumDesc) Options() pref.OptionsMessage {
230 return unmarshalOptions(ptype.X.EnumOptions(), ed.lazyInit().options)
231}
232func (ed *enumDesc) Values() pref.EnumValueDescriptors { return &ed.lazyInit().values }
233func (ed *enumDesc) ReservedNames() pref.Names { return &ed.lazyInit().resvNames }
234func (ed *enumDesc) ReservedRanges() pref.EnumRanges { return &ed.lazyInit().resvRanges }
235func (ed *enumDesc) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, ed) }
236func (ed *enumDesc) ProtoType(pref.EnumDescriptor) {}
237func (ed *enumDesc) lazyInit() *enumLazy {
238 ed.parentFile.lazyInit() // implicitly initializes enumLazy
239 return ed.lazy
240}
241
242func (ed *enumValueDesc) Options() pref.OptionsMessage {
243 return unmarshalOptions(ptype.X.EnumValueOptions(), ed.options)
244}
245func (ed *enumValueDesc) Number() pref.EnumNumber { return ed.number }
246func (ed *enumValueDesc) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, ed) }
247func (ed *enumValueDesc) ProtoType(pref.EnumValueDescriptor) {}
248
249type (
250 messageDesc struct {
251 baseDesc
252
253 enums enumDescs
254 messages messageDescs
255 extensions extensionDescs
256
257 lazy *messageLazy // protected by fileDesc.once
258 }
259 messageLazy struct {
260 typ reflect.Type
261 new func() pref.Message
262
263 isMapEntry bool
264 fields fieldDescs
265 oneofs oneofDescs
266 resvNames names
267 resvRanges fieldRanges
268 reqNumbers fieldNumbers
269 extRanges fieldRanges
270 extRangeOptions [][]byte
271 options []byte
272 }
273 fieldDesc struct {
274 baseDesc
275
276 number pref.FieldNumber
277 cardinality pref.Cardinality
278 kind pref.Kind
279 hasJSONName bool
280 jsonName string
281 hasPacked bool
282 isPacked bool
283 isWeak bool
284 isMap bool
285 defVal defaultValue
286 oneofType pref.OneofDescriptor
287 enumType pref.EnumDescriptor
288 messageType pref.MessageDescriptor
289 options []byte
290 }
291 oneofDesc struct {
292 baseDesc
293
294 fields oneofFields
295 options []byte
296 }
297)
298
299func (md *messageDesc) GoType() reflect.Type { return md.lazyInit().typ }
300func (md *messageDesc) New() pref.Message { return md.lazyInit().new() }
301func (md *messageDesc) Options() pref.OptionsMessage {
302 return unmarshalOptions(ptype.X.MessageOptions(), md.lazyInit().options)
303}
304func (md *messageDesc) IsMapEntry() bool { return md.lazyInit().isMapEntry }
305func (md *messageDesc) Fields() pref.FieldDescriptors { return &md.lazyInit().fields }
306func (md *messageDesc) Oneofs() pref.OneofDescriptors { return &md.lazyInit().oneofs }
307func (md *messageDesc) ReservedNames() pref.Names { return &md.lazyInit().resvNames }
308func (md *messageDesc) ReservedRanges() pref.FieldRanges { return &md.lazyInit().resvRanges }
309func (md *messageDesc) RequiredNumbers() pref.FieldNumbers { return &md.lazyInit().reqNumbers }
310func (md *messageDesc) ExtensionRanges() pref.FieldRanges { return &md.lazyInit().extRanges }
311func (md *messageDesc) ExtensionRangeOptions(i int) pref.OptionsMessage {
312 return unmarshalOptions(ptype.X.ExtensionRangeOptions(), md.lazyInit().extRangeOptions[i])
313}
314func (md *messageDesc) Enums() pref.EnumDescriptors { return &md.enums }
315func (md *messageDesc) Messages() pref.MessageDescriptors { return &md.messages }
316func (md *messageDesc) Extensions() pref.ExtensionDescriptors { return &md.extensions }
317func (md *messageDesc) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, md) }
318func (md *messageDesc) ProtoType(pref.MessageDescriptor) {}
319func (md *messageDesc) lazyInit() *messageLazy {
320 md.parentFile.lazyInit() // implicitly initializes messageLazy
321 return md.lazy
322}
323
324func (fd *fieldDesc) Options() pref.OptionsMessage {
325 return unmarshalOptions(ptype.X.FieldOptions(), fd.options)
326}
327func (fd *fieldDesc) Number() pref.FieldNumber { return fd.number }
328func (fd *fieldDesc) Cardinality() pref.Cardinality { return fd.cardinality }
329func (fd *fieldDesc) Kind() pref.Kind { return fd.kind }
330func (fd *fieldDesc) HasJSONName() bool { return fd.hasJSONName }
331func (fd *fieldDesc) JSONName() string { return fd.jsonName }
332func (fd *fieldDesc) IsPacked() bool { return fd.isPacked }
333func (fd *fieldDesc) IsWeak() bool { return fd.isWeak }
334func (fd *fieldDesc) IsMap() bool { return fd.isMap }
335func (fd *fieldDesc) HasDefault() bool { return fd.defVal.has }
336func (fd *fieldDesc) Default() pref.Value { return fd.defVal.get() }
337func (fd *fieldDesc) DefaultEnumValue() pref.EnumValueDescriptor { return fd.defVal.enum }
338func (fd *fieldDesc) OneofType() pref.OneofDescriptor { return fd.oneofType }
339func (fd *fieldDesc) ExtendedType() pref.MessageDescriptor { return nil }
340func (fd *fieldDesc) EnumType() pref.EnumDescriptor { return fd.enumType }
341func (fd *fieldDesc) MessageType() pref.MessageDescriptor { return fd.messageType }
342func (fd *fieldDesc) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, fd) }
343func (fd *fieldDesc) ProtoType(pref.FieldDescriptor) {}
344
345func (od *oneofDesc) Options() pref.OptionsMessage {
346 return unmarshalOptions(ptype.X.OneofOptions(), od.options)
347}
348func (od *oneofDesc) Fields() pref.FieldDescriptors { return &od.fields }
349func (od *oneofDesc) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, od) }
350func (od *oneofDesc) ProtoType(pref.OneofDescriptor) {}
351
352type (
353 extensionDesc struct {
354 baseDesc
355
356 number pref.FieldNumber
357 extendedType pref.MessageDescriptor
358
Joe Tsaiafb455e2019-03-14 16:08:22 -0700359 legacyDesc *papi.ExtensionDesc
360
Damien Neil8012b442019-01-18 09:32:24 -0800361 lazy *extensionLazy // protected by fileDesc.once
362 }
363 extensionLazy struct {
364 typ reflect.Type
365 new func() pref.Value
366 valueOf func(interface{}) pref.Value
367 interfaceOf func(pref.Value) interface{}
368
369 cardinality pref.Cardinality
370 kind pref.Kind
371 // Extensions should not have JSON names, but older versions of protoc
372 // used to set one on the descriptor. Preserve it for now to maintain
373 // the property that protoc 3.6.1 descriptors can round-trip through
374 // this package losslessly.
375 //
376 // TODO: Consider whether to drop JSONName parsing from extensions.
377 hasJSONName bool
378 jsonName string
379 isPacked bool
380 defVal defaultValue
381 enumType pref.EnumType
382 messageType pref.MessageType
383 options []byte
384 }
385)
386
387func (xd *extensionDesc) GoType() reflect.Type { return xd.lazyInit().typ }
388func (xd *extensionDesc) New() pref.Value { return xd.lazyInit().new() }
389func (xd *extensionDesc) ValueOf(v interface{}) pref.Value { return xd.lazyInit().valueOf(v) }
390func (xd *extensionDesc) InterfaceOf(v pref.Value) interface{} { return xd.lazyInit().interfaceOf(v) }
391func (xd *extensionDesc) Options() pref.OptionsMessage {
392 return unmarshalOptions(ptype.X.FieldOptions(), xd.lazyInit().options)
393}
394func (xd *extensionDesc) Number() pref.FieldNumber { return xd.number }
395func (xd *extensionDesc) Cardinality() pref.Cardinality { return xd.lazyInit().cardinality }
396func (xd *extensionDesc) Kind() pref.Kind { return xd.lazyInit().kind }
397func (xd *extensionDesc) HasJSONName() bool { return xd.lazyInit().hasJSONName }
398func (xd *extensionDesc) JSONName() string { return xd.lazyInit().jsonName }
399func (xd *extensionDesc) IsPacked() bool { return xd.lazyInit().isPacked }
400func (xd *extensionDesc) IsWeak() bool { return false }
401func (xd *extensionDesc) IsMap() bool { return false }
402func (xd *extensionDesc) HasDefault() bool { return xd.lazyInit().defVal.has }
403func (xd *extensionDesc) Default() pref.Value { return xd.lazyInit().defVal.get() }
404func (xd *extensionDesc) DefaultEnumValue() pref.EnumValueDescriptor { return xd.lazyInit().defVal.enum }
405func (xd *extensionDesc) OneofType() pref.OneofDescriptor { return nil }
406func (xd *extensionDesc) ExtendedType() pref.MessageDescriptor { return xd.extendedType }
407func (xd *extensionDesc) EnumType() pref.EnumDescriptor { return xd.lazyInit().enumType }
408func (xd *extensionDesc) MessageType() pref.MessageDescriptor { return xd.lazyInit().messageType }
409func (xd *extensionDesc) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, xd) }
410func (xd *extensionDesc) ProtoType(pref.FieldDescriptor) {}
411func (xd *extensionDesc) ProtoInternal(pragma.DoNotImplement) {}
412func (xd *extensionDesc) lazyInit() *extensionLazy {
413 xd.parentFile.lazyInit() // implicitly initializes extensionLazy
414 return xd.lazy
415}
416
Joe Tsaiafb455e2019-03-14 16:08:22 -0700417// ProtoLegacyExtensionDesc is a pseudo-internal API for allowing the v1 code
418// to be able to retrieve a v1 ExtensionDesc.
419func (xd *extensionDesc) ProtoLegacyExtensionDesc() *papi.ExtensionDesc { return xd.legacyDesc }
420
Damien Neil8012b442019-01-18 09:32:24 -0800421type (
422 serviceDesc struct {
423 baseDesc
424
425 lazy *serviceLazy // protected by fileDesc.once
426 }
427 serviceLazy struct {
428 methods methodDescs
429 options []byte
430 }
431 methodDesc struct {
432 baseDesc
433
434 inputType pref.MessageDescriptor
435 outputType pref.MessageDescriptor
436 isStreamingClient bool
437 isStreamingServer bool
438 options []byte
439 }
440)
441
442func (sd *serviceDesc) Options() pref.OptionsMessage {
443 return unmarshalOptions(ptype.X.ServiceOptions(), sd.lazyInit().options)
444}
445func (sd *serviceDesc) Methods() pref.MethodDescriptors { return &sd.lazyInit().methods }
446func (sd *serviceDesc) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, sd) }
447func (sd *serviceDesc) ProtoType(pref.ServiceDescriptor) {}
448func (sd *serviceDesc) ProtoInternal(pragma.DoNotImplement) {}
449func (sd *serviceDesc) lazyInit() *serviceLazy {
450 sd.parentFile.lazyInit() // implicitly initializes serviceLazy
451 return sd.lazy
452}
453
454func (md *methodDesc) Options() pref.OptionsMessage {
455 return unmarshalOptions(ptype.X.MethodOptions(), md.options)
456}
457func (md *methodDesc) InputType() pref.MessageDescriptor { return md.inputType }
458func (md *methodDesc) OutputType() pref.MessageDescriptor { return md.outputType }
459func (md *methodDesc) IsStreamingClient() bool { return md.isStreamingClient }
460func (md *methodDesc) IsStreamingServer() bool { return md.isStreamingServer }
461func (md *methodDesc) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, md) }
462func (md *methodDesc) ProtoType(pref.MethodDescriptor) {}
463func (md *methodDesc) ProtoInternal(pragma.DoNotImplement) {}
464
465type baseDesc struct {
466 parentFile *fileDesc
467 parent pref.Descriptor
468 index int
469 fullName
470}
471
472func (d *baseDesc) Parent() (pref.Descriptor, bool) { return d.parent, true }
473func (d *baseDesc) Index() int { return d.index }
474func (d *baseDesc) Syntax() pref.Syntax { return d.parentFile.Syntax() }
475func (d *baseDesc) IsPlaceholder() bool { return false }
476func (d *baseDesc) ProtoInternal(pragma.DoNotImplement) {}
477
478type fullName struct {
479 shortLen int
480 fullName pref.FullName
481}
482
483func (s *fullName) Name() pref.Name { return pref.Name(s.fullName[len(s.fullName)-s.shortLen:]) }
484func (s *fullName) FullName() pref.FullName { return s.fullName }
485
486func unmarshalOptions(p pref.OptionsMessage, b []byte) pref.OptionsMessage {
487 if b != nil {
488 // TODO: Consider caching the unmarshaled options message.
489 p = reflect.New(reflect.TypeOf(p).Elem()).Interface().(pref.OptionsMessage)
490 if err := proto.Unmarshal(b, p.(proto.Message)); err != nil {
491 panic(err)
492 }
493 }
494 return p.(proto.Message)
495}