blob: 0363cf02420d1c182534c6f4311c6f10e75ee3a4 [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001package protoprint
2
3import (
4 "bytes"
5 "fmt"
6 "io"
7 "math"
8 "os"
9 "path/filepath"
10 "reflect"
11 "sort"
12 "strings"
13
14 "github.com/golang/protobuf/proto"
15 "github.com/golang/protobuf/protoc-gen-go/descriptor"
16
17 "github.com/jhump/protoreflect/desc"
18 "github.com/jhump/protoreflect/desc/internal"
19 "github.com/jhump/protoreflect/dynamic"
20)
21
22// Printer knows how to format file descriptors as proto source code. Its fields
23// provide some control over how the resulting source file is constructed and
24// formatted.
25type Printer struct {
26 // If true, comments are rendered using "/*" style comments. Otherwise, they
27 // are printed using "//" style line comments.
28 PreferMultiLineStyleComments bool
29
30 // If true, elements are sorted into a canonical order.
31 //
32 // The canonical order for elements in a file follows:
33 // 1. Syntax
34 // 2. Package
35 // 3. Imports (sorted lexically)
36 // 4. Options (sorted by name, standard options before custom options)
37 // 5. Messages (sorted by name)
38 // 6. Enums (sorted by name)
39 // 7. Services (sorted by name)
40 // 8. Extensions (grouped by extendee, sorted by extendee+tag)
41 //
42 // The canonical order of elements in a message follows:
43 // 1. Options (sorted by name, standard options before custom options)
44 // 2. Fields and One-Ofs (sorted by tag; one-ofs interleaved based on the
45 // minimum tag therein)
46 // 3. Nested Messages (sorted by name)
47 // 4. Nested Enums (sorted by name)
48 // 5. Extension ranges (sorted by starting tag number)
49 // 6. Nested Extensions (grouped by extendee, sorted by extendee+tag)
50 // 7. Reserved ranges (sorted by starting tag number)
51 // 8. Reserved names (sorted lexically)
52 //
53 // Methods are sorted within a service by name and appear after any service
54 // options (which are sorted by name, standard options before custom ones).
55 // Enum values are sorted within an enum, first by numeric value then by
56 // name, and also appear after any enum options.
57 //
58 // Options for fields, enum values, and extension ranges are sorted by name,
59 // standard options before custom ones.
60 SortElements bool
61
62 // The indentation used. Any characters other than spaces or tabs will be
63 // replaced with spaces. If unset/empty, two spaces will be used.
64 Indent string
65
66 // If true, detached comments (between elements) will be ignored.
67 //
68 // Deprecated: Use OmitComments bitmask instead.
69 OmitDetachedComments bool
70
71 // A bitmask of comment types to omit. If unset, all comments will be
72 // included. Use CommentsAll to not print any comments.
73 OmitComments CommentType
74
75 // If true, trailing comments that typically appear on the same line as an
76 // element (option, field, enum value, method) will be printed on a separate
77 // line instead.
78 //
79 // So, with this set, you'll get output like so:
80 //
81 // // leading comment for field
82 // repeated string names = 1;
83 // // trailing comment
84 //
85 // If left false, the printer will try to emit trailing comments on the same
86 // line instead:
87 //
88 // // leading comment for field
89 // repeated string names = 1; // trailing comment
90 //
91 // If the trailing comment has more than one line, it will automatically be
92 // forced to the next line. Also, elements that end with "}" instead of ";"
93 // will have trailing comments rendered on the subsequent line.
94 TrailingCommentsOnSeparateLine bool
95
96 // If true, the printed output will eschew any blank lines, which otherwise
Scott Baker4a35a702019-11-26 08:17:33 -080097 // appear between descriptor elements and comment blocks. Note that if
Zack Williamse940c7a2019-08-21 14:25:39 -070098 // detached comments are being printed, this will cause them to be merged
99 // into the subsequent leading comments. Similarly, any element trailing
100 // comments will be merged into the subsequent leading comments.
101 Compact bool
102
103 // If true, all references to messages, extensions, and enums (such as in
104 // options, field types, and method request and response types) will be
105 // fully-qualified. When left unset, the referenced elements will contain
106 // only as much qualifier as is required.
107 //
108 // For example, if a message is in the same package as the reference, the
109 // simple name can be used. If a message shares some context with the
110 // reference, only the unshared context needs to be included. For example:
111 //
112 // message Foo {
113 // message Bar {
114 // enum Baz {
115 // ZERO = 0;
116 // ONE = 1;
117 // }
118 // }
119 //
120 // // This field shares some context as the enum it references: they are
121 // // both inside of the namespace Foo:
122 // // field is "Foo.my_baz"
123 // // enum is "Foo.Bar.Baz"
124 // // So we only need to qualify the reference with the context that they
125 // // do NOT have in common:
126 // Bar.Baz my_baz = 1;
127 // }
128 //
129 // When printing fully-qualified names, they will be preceded by a dot, to
130 // avoid any ambiguity that they might be relative vs. fully-qualified.
131 ForceFullyQualifiedNames bool
132}
133
134// CommentType is a kind of comments in a proto source file. This can be used
135// as a bitmask.
136type CommentType int
137
138const (
139 // CommentsDetached refers to comments that are not "attached" to any
140 // source element. They are attributed to the subsequent element in the
141 // file as "detached" comments.
142 CommentsDetached CommentType = 1 << iota
143 // CommentsTrailing refers to a comment block immediately following an
144 // element in the source file. If another element immediately follows
145 // the trailing comment, it is instead considered a leading comment for
146 // that subsequent element.
147 CommentsTrailing
148 // CommentsLeading refers to a comment block immediately preceding an
149 // element in the source file. For high-level elements (those that have
150 // their own descriptor), these are used as doc comments for that element.
151 CommentsLeading
152 // CommentsTokens refers to any comments (leading, trailing, or detached)
153 // on low-level elements in the file. "High-level" elements have their own
154 // descriptors, e.g. messages, enums, fields, services, and methods. But
155 // comments can appear anywhere (such as around identifiers and keywords,
156 // sprinkled inside the declarations of a high-level element). This class
157 // of comments are for those extra comments sprinkled into the file.
158 CommentsTokens
159
160 // CommentsNonDoc refers to comments that are *not* doc comments. This is a
161 // bitwise union of everything other than CommentsLeading. If you configure
162 // a printer to omit this, only doc comments on descriptor elements will be
163 // included in the printed output.
164 CommentsNonDoc = CommentsDetached | CommentsTrailing | CommentsTokens
165 // CommentsAll indicates all kinds of comments. If you configure a printer
166 // to omit this, no comments will appear in the printed output, even if the
167 // input descriptors had source info and comments.
168 CommentsAll = -1
169)
170
171// PrintProtoFiles prints all of the given file descriptors. The given open
172// function is given a file name and is responsible for creating the outputs and
173// returning the corresponding writer.
174func (p *Printer) PrintProtoFiles(fds []*desc.FileDescriptor, open func(name string) (io.WriteCloser, error)) error {
175 for _, fd := range fds {
176 w, err := open(fd.GetName())
177 if err != nil {
178 return fmt.Errorf("failed to open %s: %v", fd.GetName(), err)
179 }
180 err = func() error {
181 defer w.Close()
182 return p.PrintProtoFile(fd, w)
183 }()
184 if err != nil {
185 return fmt.Errorf("failed to write %s: %v", fd.GetName(), err)
186 }
187 }
188 return nil
189}
190
191// PrintProtosToFileSystem prints all of the given file descriptors to files in
192// the given directory. If file names in the given descriptors include path
193// information, they will be relative to the given root.
194func (p *Printer) PrintProtosToFileSystem(fds []*desc.FileDescriptor, rootDir string) error {
195 return p.PrintProtoFiles(fds, func(name string) (io.WriteCloser, error) {
196 fullPath := filepath.Join(rootDir, name)
197 dir := filepath.Dir(fullPath)
198 if err := os.MkdirAll(dir, os.ModePerm); err != nil {
199 return nil, err
200 }
201 return os.OpenFile(fullPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
202 })
203}
204
205// pkg represents a package name
206type pkg string
207
208// imp represents an imported file name
209type imp string
210
211// ident represents an identifier
212type ident string
213
214// option represents a resolved descriptor option
215type option struct {
216 name string
217 val interface{}
218}
219
220// reservedRange represents a reserved range from a message or enum
221type reservedRange struct {
222 start, end int32
223}
224
225// PrintProtoFile prints the given single file descriptor to the given writer.
226func (p *Printer) PrintProtoFile(fd *desc.FileDescriptor, out io.Writer) error {
227 return p.printProto(fd, out)
228}
229
230// PrintProto prints the given descriptor and returns the resulting string. This
231// can be used to print proto files, but it can also be used to get the proto
232// "source form" for any kind of descriptor, which can be a more user-friendly
233// way to present descriptors that are intended for human consumption.
234func (p *Printer) PrintProtoToString(dsc desc.Descriptor) (string, error) {
235 var buf bytes.Buffer
236 if err := p.printProto(dsc, &buf); err != nil {
237 return "", err
238 }
239 return buf.String(), nil
240}
241
242func (p *Printer) printProto(dsc desc.Descriptor, out io.Writer) error {
243 w := newWriter(out)
244
245 if p.Indent == "" {
246 // default indent to two spaces
247 p.Indent = " "
248 } else {
249 // indent must be all spaces or tabs, so convert other chars to spaces
250 ind := make([]rune, 0, len(p.Indent))
251 for _, r := range p.Indent {
252 if r == '\t' {
253 ind = append(ind, r)
254 } else {
255 ind = append(ind, ' ')
256 }
257 }
258 p.Indent = string(ind)
259 }
260 if p.OmitDetachedComments {
261 p.OmitComments |= CommentsDetached
262 }
263
264 er := dynamic.ExtensionRegistry{}
265 er.AddExtensionsFromFileRecursively(dsc.GetFile())
266 mf := dynamic.NewMessageFactoryWithExtensionRegistry(&er)
267 fdp := dsc.GetFile().AsFileDescriptorProto()
268 sourceInfo := internal.CreateSourceInfoMap(fdp)
Scott Baker4a35a702019-11-26 08:17:33 -0800269 extendOptionLocations(sourceInfo, fdp.GetSourceCodeInfo().GetLocation())
Zack Williamse940c7a2019-08-21 14:25:39 -0700270
271 path := findElement(dsc)
272 switch d := dsc.(type) {
273 case *desc.FileDescriptor:
274 p.printFile(d, mf, w, sourceInfo)
275 case *desc.MessageDescriptor:
276 p.printMessage(d, mf, w, sourceInfo, path, 0)
277 case *desc.FieldDescriptor:
278 var scope string
279 if md, ok := d.GetParent().(*desc.MessageDescriptor); ok {
280 scope = md.GetFullyQualifiedName()
281 } else {
282 scope = d.GetFile().GetPackage()
283 }
284 if d.IsExtension() {
285 fmt.Fprint(w, "extend ")
286 extNameSi := sourceInfo.Get(append(path, internal.Field_extendeeTag))
287 p.printElementString(extNameSi, w, 0, p.qualifyName(d.GetFile().GetPackage(), scope, d.GetOwner().GetFullyQualifiedName()))
288 fmt.Fprintln(w, "{")
289
290 p.printField(d, mf, w, sourceInfo, path, scope, 1)
291
292 fmt.Fprintln(w, "}")
293 } else {
294 p.printField(d, mf, w, sourceInfo, path, scope, 0)
295 }
296 case *desc.OneOfDescriptor:
297 md := d.GetOwner()
298 elements := elementAddrs{dsc: md}
299 for i := range md.GetFields() {
300 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_fieldsTag, elementIndex: i})
301 }
302 p.printOneOf(d, elements, 0, mf, w, sourceInfo, path[:len(path)-1], 0, path[len(path)-1])
303 case *desc.EnumDescriptor:
304 p.printEnum(d, mf, w, sourceInfo, path, 0)
305 case *desc.EnumValueDescriptor:
306 p.printEnumValue(d, mf, w, sourceInfo, path, 0)
307 case *desc.ServiceDescriptor:
308 p.printService(d, mf, w, sourceInfo, path, 0)
309 case *desc.MethodDescriptor:
310 p.printMethod(d, mf, w, sourceInfo, path, 0)
311 }
312
313 return w.err
314}
315
316func findElement(dsc desc.Descriptor) []int32 {
317 if dsc.GetParent() == nil {
318 return nil
319 }
320 path := findElement(dsc.GetParent())
321 switch d := dsc.(type) {
322 case *desc.MessageDescriptor:
323 if pm, ok := d.GetParent().(*desc.MessageDescriptor); ok {
324 return append(path, internal.Message_nestedMessagesTag, getMessageIndex(d, pm.GetNestedMessageTypes()))
325 }
326 return append(path, internal.File_messagesTag, getMessageIndex(d, d.GetFile().GetMessageTypes()))
327
328 case *desc.FieldDescriptor:
329 if d.IsExtension() {
330 if pm, ok := d.GetParent().(*desc.MessageDescriptor); ok {
331 return append(path, internal.Message_extensionsTag, getFieldIndex(d, pm.GetNestedExtensions()))
332 }
333 return append(path, internal.File_extensionsTag, getFieldIndex(d, d.GetFile().GetExtensions()))
334 }
335 return append(path, internal.Message_fieldsTag, getFieldIndex(d, d.GetOwner().GetFields()))
336
337 case *desc.OneOfDescriptor:
338 return append(path, internal.Message_oneOfsTag, getOneOfIndex(d, d.GetOwner().GetOneOfs()))
339
340 case *desc.EnumDescriptor:
341 if pm, ok := d.GetParent().(*desc.MessageDescriptor); ok {
342 return append(path, internal.Message_enumsTag, getEnumIndex(d, pm.GetNestedEnumTypes()))
343 }
344 return append(path, internal.File_enumsTag, getEnumIndex(d, d.GetFile().GetEnumTypes()))
345
346 case *desc.EnumValueDescriptor:
347 return append(path, internal.Enum_valuesTag, getEnumValueIndex(d, d.GetEnum().GetValues()))
348
349 case *desc.ServiceDescriptor:
350 return append(path, internal.File_servicesTag, getServiceIndex(d, d.GetFile().GetServices()))
351
352 case *desc.MethodDescriptor:
353 return append(path, internal.Service_methodsTag, getMethodIndex(d, d.GetService().GetMethods()))
354
355 default:
356 panic(fmt.Sprintf("unexpected descriptor type: %T", dsc))
357 }
358}
359
360func getMessageIndex(md *desc.MessageDescriptor, list []*desc.MessageDescriptor) int32 {
361 for i := range list {
362 if md == list[i] {
363 return int32(i)
364 }
365 }
366 panic(fmt.Sprintf("unable to determine index of message %s", md.GetFullyQualifiedName()))
367}
368
369func getFieldIndex(fd *desc.FieldDescriptor, list []*desc.FieldDescriptor) int32 {
370 for i := range list {
371 if fd == list[i] {
372 return int32(i)
373 }
374 }
375 panic(fmt.Sprintf("unable to determine index of field %s", fd.GetFullyQualifiedName()))
376}
377
378func getOneOfIndex(ood *desc.OneOfDescriptor, list []*desc.OneOfDescriptor) int32 {
379 for i := range list {
380 if ood == list[i] {
381 return int32(i)
382 }
383 }
384 panic(fmt.Sprintf("unable to determine index of oneof %s", ood.GetFullyQualifiedName()))
385}
386
387func getEnumIndex(ed *desc.EnumDescriptor, list []*desc.EnumDescriptor) int32 {
388 for i := range list {
389 if ed == list[i] {
390 return int32(i)
391 }
392 }
393 panic(fmt.Sprintf("unable to determine index of enum %s", ed.GetFullyQualifiedName()))
394}
395
396func getEnumValueIndex(evd *desc.EnumValueDescriptor, list []*desc.EnumValueDescriptor) int32 {
397 for i := range list {
398 if evd == list[i] {
399 return int32(i)
400 }
401 }
402 panic(fmt.Sprintf("unable to determine index of enum value %s", evd.GetFullyQualifiedName()))
403}
404
405func getServiceIndex(sd *desc.ServiceDescriptor, list []*desc.ServiceDescriptor) int32 {
406 for i := range list {
407 if sd == list[i] {
408 return int32(i)
409 }
410 }
411 panic(fmt.Sprintf("unable to determine index of service %s", sd.GetFullyQualifiedName()))
412}
413
414func getMethodIndex(mtd *desc.MethodDescriptor, list []*desc.MethodDescriptor) int32 {
415 for i := range list {
416 if mtd == list[i] {
417 return int32(i)
418 }
419 }
420 panic(fmt.Sprintf("unable to determine index of method %s", mtd.GetFullyQualifiedName()))
421}
422
423func (p *Printer) newLine(w io.Writer) {
424 if !p.Compact {
425 fmt.Fprintln(w)
426 }
427}
428
429func (p *Printer) printFile(fd *desc.FileDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap) {
430 opts, err := p.extractOptions(fd, fd.GetOptions(), mf)
431 if err != nil {
432 return
433 }
434
435 fdp := fd.AsFileDescriptorProto()
436 path := make([]int32, 1)
437
438 path[0] = internal.File_packageTag
439 sourceInfo.PutIfAbsent(append(path, 0), sourceInfo.Get(path))
440
441 path[0] = internal.File_syntaxTag
442 si := sourceInfo.Get(path)
443 p.printElement(false, si, w, 0, func(w *writer) {
444 syn := fdp.GetSyntax()
445 if syn == "" {
446 syn = "proto2"
447 }
448 fmt.Fprintf(w, "syntax = %q;", syn)
449 })
450 p.newLine(w)
451
Scott Baker4a35a702019-11-26 08:17:33 -0800452 skip := map[interface{}]bool{}
453
Zack Williamse940c7a2019-08-21 14:25:39 -0700454 elements := elementAddrs{dsc: fd, opts: opts}
455 if fdp.Package != nil {
456 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_packageTag, elementIndex: 0, order: -3})
457 }
458 for i := range fd.AsFileDescriptorProto().GetDependency() {
459 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_dependencyTag, elementIndex: i, order: -2})
460 }
461 elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.File_optionsTag, -1, opts)...)
462 for i := range fd.GetMessageTypes() {
463 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_messagesTag, elementIndex: i})
464 }
465 for i := range fd.GetEnumTypes() {
466 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_enumsTag, elementIndex: i})
467 }
468 for i := range fd.GetServices() {
469 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_servicesTag, elementIndex: i})
470 }
Scott Baker4a35a702019-11-26 08:17:33 -0800471 exts := p.computeExtensions(sourceInfo, fd.GetExtensions(), []int32{internal.File_extensionsTag})
472 for i, extd := range fd.GetExtensions() {
473 if extd.GetType() == descriptor.FieldDescriptorProto_TYPE_GROUP {
474 // we don't emit nested messages for groups since
475 // they get special treatment
476 skip[extd.GetMessageType()] = true
477 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700478 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_extensionsTag, elementIndex: i})
479 }
480
481 p.sort(elements, sourceInfo, nil)
482
483 pkgName := fd.GetPackage()
484
Zack Williamse940c7a2019-08-21 14:25:39 -0700485 for i, el := range elements.addrs {
486 d := elements.at(el)
Scott Baker4a35a702019-11-26 08:17:33 -0800487
488 // skip[d] will panic if d is a slice (which it could be for []option),
489 // so just ignore it since we don't try to skip options
490 if reflect.TypeOf(d).Kind() != reflect.Slice && skip[d] {
491 // skip this element
492 continue
493 }
494
495 if i > 0 {
496 p.newLine(w)
497 }
498
Zack Williamse940c7a2019-08-21 14:25:39 -0700499 path = []int32{el.elementType, int32(el.elementIndex)}
Zack Williamse940c7a2019-08-21 14:25:39 -0700500
Scott Baker4a35a702019-11-26 08:17:33 -0800501 switch d := d.(type) {
502 case pkg:
503 si := sourceInfo.Get(path)
504 p.printElement(false, si, w, 0, func(w *writer) {
505 fmt.Fprintf(w, "package %s;", d)
506 })
507 case imp:
508 si := sourceInfo.Get(path)
509 p.printElement(false, si, w, 0, func(w *writer) {
510 fmt.Fprintf(w, "import %q;", d)
511 })
512 case []option:
513 p.printOptionsLong(d, w, sourceInfo, path, 0)
514 case *desc.MessageDescriptor:
515 p.printMessage(d, mf, w, sourceInfo, path, 0)
516 case *desc.EnumDescriptor:
517 p.printEnum(d, mf, w, sourceInfo, path, 0)
518 case *desc.ServiceDescriptor:
519 p.printService(d, mf, w, sourceInfo, path, 0)
520 case *desc.FieldDescriptor:
521 extDecl := exts[d]
522 p.printExtensions(extDecl, exts, elements, i, mf, w, sourceInfo, nil, internal.File_extensionsTag, pkgName, pkgName, 0)
523 // we printed all extensions in the group, so we can skip the others
524 for _, fld := range extDecl.fields {
525 skip[fld] = true
Zack Williamse940c7a2019-08-21 14:25:39 -0700526 }
527 }
528 }
Scott Baker4a35a702019-11-26 08:17:33 -0800529}
Zack Williamse940c7a2019-08-21 14:25:39 -0700530
Scott Baker4a35a702019-11-26 08:17:33 -0800531func findExtSi(fieldSi *descriptor.SourceCodeInfo_Location, extSis []*descriptor.SourceCodeInfo_Location) *descriptor.SourceCodeInfo_Location {
532 if len(fieldSi.GetSpan()) == 0 {
533 return nil
Zack Williamse940c7a2019-08-21 14:25:39 -0700534 }
Scott Baker4a35a702019-11-26 08:17:33 -0800535 for _, extSi := range extSis {
536 if isSpanWithin(fieldSi.Span, extSi.Span) {
537 return extSi
538 }
539 }
540 return nil
541}
542
543func isSpanWithin(span, enclosing []int32) bool {
544 start := enclosing[0]
545 var end int32
546 if len(enclosing) == 3 {
547 end = enclosing[0]
548 } else {
549 end = enclosing[2]
550 }
551 if span[0] < start || span[0] > end {
552 return false
553 }
554
555 if span[0] == start {
556 return span[1] >= enclosing[1]
557 } else if span[0] == end {
558 return span[1] <= enclosing[len(enclosing)-1]
559 }
560 return true
561}
562
563type extensionDecl struct {
564 extendee string
565 sourceInfo *descriptor.SourceCodeInfo_Location
566 fields []*desc.FieldDescriptor
567}
568
569type extensions map[*desc.FieldDescriptor]*extensionDecl
570
571func (p *Printer) computeExtensions(sourceInfo internal.SourceInfoMap, exts []*desc.FieldDescriptor, path []int32) extensions {
572 extsMap := map[string]map[*descriptor.SourceCodeInfo_Location]*extensionDecl{}
573 extSis := sourceInfo.GetAll(path)
574 for _, extd := range exts {
575 name := extd.GetOwner().GetFullyQualifiedName()
576 extSi := findExtSi(extd.GetSourceInfo(), extSis)
577 extsBySi := extsMap[name]
578 if extsBySi == nil {
579 extsBySi = map[*descriptor.SourceCodeInfo_Location]*extensionDecl{}
580 extsMap[name] = extsBySi
581 }
582 extDecl := extsBySi[extSi]
583 if extDecl == nil {
584 extDecl = &extensionDecl{
585 sourceInfo: extSi,
586 extendee: name,
587 }
588 extsBySi[extSi] = extDecl
589 }
590 extDecl.fields = append(extDecl.fields, extd)
591 }
592
593 ret := extensions{}
594 for _, extsBySi := range extsMap {
595 for _, extDecl := range extsBySi {
596 for _, extd := range extDecl.fields {
597 ret[extd] = extDecl
598 }
599 }
600 }
601 return ret
Zack Williamse940c7a2019-08-21 14:25:39 -0700602}
603
604func (p *Printer) sort(elements elementAddrs, sourceInfo internal.SourceInfoMap, path []int32) {
605 if p.SortElements {
606 // canonical sorted order
607 sort.Stable(elements)
608 } else {
609 // use source order (per location information in SourceCodeInfo); or
610 // if that isn't present use declaration order, but grouped by type
611 sort.Stable(elementSrcOrder{
612 elementAddrs: elements,
613 sourceInfo: sourceInfo,
614 prefix: path,
615 })
616 }
617}
618
619func (p *Printer) qualifyName(pkg, scope string, fqn string) string {
620 if p.ForceFullyQualifiedNames {
621 // forcing fully-qualified names; make sure to include preceding dot
622 if fqn[0] == '.' {
623 return fqn
624 }
625 return fmt.Sprintf(".%s", fqn)
626 }
627
628 // compute relative name (so no leading dot)
629 if fqn[0] == '.' {
630 fqn = fqn[1:]
631 }
632 if len(scope) > 0 && scope[len(scope)-1] != '.' {
633 scope = scope + "."
634 }
635 for scope != "" {
636 if strings.HasPrefix(fqn, scope) {
637 return fqn[len(scope):]
638 }
639 if scope == pkg+"." {
640 break
641 }
642 pos := strings.LastIndex(scope[:len(scope)-1], ".")
643 scope = scope[:pos+1]
644 }
645 return fqn
646}
647
648func (p *Printer) typeString(fld *desc.FieldDescriptor, scope string) string {
649 if fld.IsMap() {
650 return fmt.Sprintf("map<%s, %s>", p.typeString(fld.GetMapKeyType(), scope), p.typeString(fld.GetMapValueType(), scope))
651 }
652 switch fld.GetType() {
653 case descriptor.FieldDescriptorProto_TYPE_INT32:
654 return "int32"
655 case descriptor.FieldDescriptorProto_TYPE_INT64:
656 return "int64"
657 case descriptor.FieldDescriptorProto_TYPE_UINT32:
658 return "uint32"
659 case descriptor.FieldDescriptorProto_TYPE_UINT64:
660 return "uint64"
661 case descriptor.FieldDescriptorProto_TYPE_SINT32:
662 return "sint32"
663 case descriptor.FieldDescriptorProto_TYPE_SINT64:
664 return "sint64"
665 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
666 return "fixed32"
667 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
668 return "fixed64"
669 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
670 return "sfixed32"
671 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
672 return "sfixed64"
673 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
674 return "float"
675 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
676 return "double"
677 case descriptor.FieldDescriptorProto_TYPE_BOOL:
678 return "bool"
679 case descriptor.FieldDescriptorProto_TYPE_STRING:
680 return "string"
681 case descriptor.FieldDescriptorProto_TYPE_BYTES:
682 return "bytes"
683 case descriptor.FieldDescriptorProto_TYPE_ENUM:
684 return p.qualifyName(fld.GetFile().GetPackage(), scope, fld.GetEnumType().GetFullyQualifiedName())
685 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
686 return p.qualifyName(fld.GetFile().GetPackage(), scope, fld.GetMessageType().GetFullyQualifiedName())
687 case descriptor.FieldDescriptorProto_TYPE_GROUP:
688 return fld.GetMessageType().GetName()
689 }
690 panic(fmt.Sprintf("invalid type: %v", fld.GetType()))
691}
692
693func (p *Printer) printMessage(md *desc.MessageDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
694 si := sourceInfo.Get(path)
695 p.printElement(true, si, w, indent, func(w *writer) {
696 p.indent(w, indent)
697
698 fmt.Fprint(w, "message ")
699 nameSi := sourceInfo.Get(append(path, internal.Message_nameTag))
700 p.printElementString(nameSi, w, indent, md.GetName())
701 fmt.Fprintln(w, "{")
702
703 p.printMessageBody(md, mf, w, sourceInfo, path, indent+1)
704 p.indent(w, indent)
705 fmt.Fprintln(w, "}")
706 })
707}
708
709func (p *Printer) printMessageBody(md *desc.MessageDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
710 opts, err := p.extractOptions(md, md.GetOptions(), mf)
711 if err != nil {
712 if w.err == nil {
713 w.err = err
714 }
715 return
716 }
717
718 skip := map[interface{}]bool{}
719
720 elements := elementAddrs{dsc: md, opts: opts}
721 elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.Message_optionsTag, -1, opts)...)
722 for i := range md.AsDescriptorProto().GetReservedRange() {
723 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_reservedRangeTag, elementIndex: i})
724 }
725 for i := range md.AsDescriptorProto().GetReservedName() {
726 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_reservedNameTag, elementIndex: i})
727 }
728 for i := range md.AsDescriptorProto().GetExtensionRange() {
729 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_extensionRangeTag, elementIndex: i})
730 }
731 for i, fld := range md.GetFields() {
732 if fld.IsMap() || fld.GetType() == descriptor.FieldDescriptorProto_TYPE_GROUP {
733 // we don't emit nested messages for map types or groups since
734 // they get special treatment
735 skip[fld.GetMessageType()] = true
736 }
737 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_fieldsTag, elementIndex: i})
738 }
739 for i := range md.GetNestedMessageTypes() {
740 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_nestedMessagesTag, elementIndex: i})
741 }
742 for i := range md.GetNestedEnumTypes() {
743 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_enumsTag, elementIndex: i})
744 }
Scott Baker4a35a702019-11-26 08:17:33 -0800745 exts := p.computeExtensions(sourceInfo, md.GetNestedExtensions(), append(path, internal.Message_extensionsTag))
746 for i, extd := range md.GetNestedExtensions() {
747 if extd.GetType() == descriptor.FieldDescriptorProto_TYPE_GROUP {
748 // we don't emit nested messages for groups since
749 // they get special treatment
750 skip[extd.GetMessageType()] = true
751 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700752 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_extensionsTag, elementIndex: i})
753 }
754
755 p.sort(elements, sourceInfo, path)
756
757 pkg := md.GetFile().GetPackage()
758 scope := md.GetFullyQualifiedName()
759
Zack Williamse940c7a2019-08-21 14:25:39 -0700760 for i, el := range elements.addrs {
761 d := elements.at(el)
Scott Baker4a35a702019-11-26 08:17:33 -0800762
Zack Williamse940c7a2019-08-21 14:25:39 -0700763 // skip[d] will panic if d is a slice (which it could be for []option),
764 // so just ignore it since we don't try to skip options
765 if reflect.TypeOf(d).Kind() != reflect.Slice && skip[d] {
766 // skip this element
767 continue
768 }
769
Scott Baker4a35a702019-11-26 08:17:33 -0800770 if i > 0 {
771 p.newLine(w)
772 }
773
Zack Williamse940c7a2019-08-21 14:25:39 -0700774 childPath := append(path, el.elementType, int32(el.elementIndex))
Zack Williamse940c7a2019-08-21 14:25:39 -0700775
Scott Baker4a35a702019-11-26 08:17:33 -0800776 switch d := d.(type) {
777 case []option:
778 p.printOptionsLong(d, w, sourceInfo, childPath, indent)
779 case *desc.FieldDescriptor:
780 if d.IsExtension() {
781 extDecl := exts[d]
782 p.printExtensions(extDecl, exts, elements, i, mf, w, sourceInfo, path, internal.Message_extensionsTag, pkg, scope, indent)
783 // we printed all extensions in the group, so we can skip the others
784 for _, fld := range extDecl.fields {
785 skip[fld] = true
786 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700787 } else {
Zack Williamse940c7a2019-08-21 14:25:39 -0700788 ood := d.GetOneOf()
789 if ood == nil {
790 p.printField(d, mf, w, sourceInfo, childPath, scope, indent)
Scott Baker4a35a702019-11-26 08:17:33 -0800791 } else {
Zack Williamse940c7a2019-08-21 14:25:39 -0700792 // print the one-of, including all of its fields
793 p.printOneOf(ood, elements, i, mf, w, sourceInfo, path, indent, d.AsFieldDescriptorProto().GetOneofIndex())
Scott Baker4a35a702019-11-26 08:17:33 -0800794 for _, fld := range ood.GetChoices() {
795 skip[fld] = true
Zack Williamse940c7a2019-08-21 14:25:39 -0700796 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700797 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700798 }
Scott Baker4a35a702019-11-26 08:17:33 -0800799 case *desc.MessageDescriptor:
800 p.printMessage(d, mf, w, sourceInfo, childPath, indent)
801 case *desc.EnumDescriptor:
802 p.printEnum(d, mf, w, sourceInfo, childPath, indent)
803 case *descriptor.DescriptorProto_ExtensionRange:
804 // collapse ranges into a single "extensions" block
805 ranges := []*descriptor.DescriptorProto_ExtensionRange{d}
806 addrs := []elementAddr{el}
807 for idx := i + 1; idx < len(elements.addrs); idx++ {
808 elnext := elements.addrs[idx]
809 if elnext.elementType != el.elementType {
810 break
811 }
812 extr := elements.at(elnext).(*descriptor.DescriptorProto_ExtensionRange)
813 if !areEqual(d.Options, extr.Options, mf) {
814 break
815 }
816 ranges = append(ranges, extr)
817 addrs = append(addrs, elnext)
818 skip[extr] = true
819 }
820 p.printExtensionRanges(md, ranges, addrs, mf, w, sourceInfo, path, indent)
821 case reservedRange:
822 // collapse reserved ranges into a single "reserved" block
823 ranges := []reservedRange{d}
824 addrs := []elementAddr{el}
825 for idx := i + 1; idx < len(elements.addrs); idx++ {
826 elnext := elements.addrs[idx]
827 if elnext.elementType != el.elementType {
828 break
829 }
830 rr := elements.at(elnext).(reservedRange)
831 ranges = append(ranges, rr)
832 addrs = append(addrs, elnext)
833 skip[rr] = true
834 }
835 p.printReservedRanges(ranges, false, addrs, w, sourceInfo, path, indent)
836 case string: // reserved name
837 // collapse reserved names into a single "reserved" block
838 names := []string{d}
839 addrs := []elementAddr{el}
840 for idx := i + 1; idx < len(elements.addrs); idx++ {
841 elnext := elements.addrs[idx]
842 if elnext.elementType != el.elementType {
843 break
844 }
845 rn := elements.at(elnext).(string)
846 names = append(names, rn)
847 addrs = append(addrs, elnext)
848 skip[rn] = true
849 }
850 p.printReservedNames(names, addrs, w, sourceInfo, path, indent)
Zack Williamse940c7a2019-08-21 14:25:39 -0700851 }
852 }
Zack Williamse940c7a2019-08-21 14:25:39 -0700853}
854
855func areEqual(a, b proto.Message, mf *dynamic.MessageFactory) bool {
856 // proto.Equal doesn't handle unknown extensions very well :(
857 // so we convert to a dynamic message (which should know about all extensions via
858 // extension registry) and then compare
859 return dynamic.MessagesEqual(asDynamicIfPossible(a, mf), asDynamicIfPossible(b, mf))
860}
861
862func asDynamicIfPossible(msg proto.Message, mf *dynamic.MessageFactory) proto.Message {
863 if dm, ok := msg.(*dynamic.Message); ok {
864 return dm
865 } else {
866 md, err := desc.LoadMessageDescriptorForMessage(msg)
867 if err == nil {
868 dm := mf.NewDynamicMessage(md)
869 if dm.ConvertFrom(msg) == nil {
870 return dm
871 }
872 }
873 }
874 return msg
875}
876
877func (p *Printer) printField(fld *desc.FieldDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, scope string, indent int) {
878 var groupPath []int32
879 var si *descriptor.SourceCodeInfo_Location
880 if isGroup(fld) {
881 // compute path to group message type
882 groupPath = make([]int32, len(path)-2)
883 copy(groupPath, path)
Scott Baker4a35a702019-11-26 08:17:33 -0800884
885 var candidates []*desc.MessageDescriptor
886 var parentTag int32
887 switch parent := fld.GetParent().(type) {
888 case *desc.MessageDescriptor:
889 // group in a message
890 candidates = parent.GetNestedMessageTypes()
891 parentTag = internal.Message_nestedMessagesTag
892 case *desc.FileDescriptor:
893 // group that is a top-level extension
894 candidates = parent.GetMessageTypes()
895 parentTag = internal.File_messagesTag
896 }
897
Zack Williamse940c7a2019-08-21 14:25:39 -0700898 var groupMsgIndex int32
Scott Baker4a35a702019-11-26 08:17:33 -0800899 for i, nmd := range candidates {
Zack Williamse940c7a2019-08-21 14:25:39 -0700900 if nmd == fld.GetMessageType() {
901 // found it
902 groupMsgIndex = int32(i)
903 break
904 }
905 }
Scott Baker4a35a702019-11-26 08:17:33 -0800906 groupPath = append(groupPath, parentTag, groupMsgIndex)
Zack Williamse940c7a2019-08-21 14:25:39 -0700907
908 // the group message is where the field's comments and position are stored
909 si = sourceInfo.Get(groupPath)
910 } else {
911 si = sourceInfo.Get(path)
912 }
913
914 p.printElement(true, si, w, indent, func(w *writer) {
915 p.indent(w, indent)
916 if shouldEmitLabel(fld) {
917 locSi := sourceInfo.Get(append(path, internal.Field_labelTag))
918 p.printElementString(locSi, w, indent, labelString(fld.GetLabel()))
919 }
920
921 if isGroup(fld) {
922 fmt.Fprint(w, "group ")
923
924 typeSi := sourceInfo.Get(append(path, internal.Field_typeTag))
925 p.printElementString(typeSi, w, indent, p.typeString(fld, scope))
926 fmt.Fprint(w, "= ")
927
928 numSi := sourceInfo.Get(append(path, internal.Field_numberTag))
929 p.printElementString(numSi, w, indent, fmt.Sprintf("%d", fld.GetNumber()))
930
931 fmt.Fprintln(w, "{")
932 p.printMessageBody(fld.GetMessageType(), mf, w, sourceInfo, groupPath, indent+1)
933
934 p.indent(w, indent)
935 fmt.Fprintln(w, "}")
936 } else {
937 typeSi := sourceInfo.Get(append(path, internal.Field_typeTag))
938 p.printElementString(typeSi, w, indent, p.typeString(fld, scope))
939
940 nameSi := sourceInfo.Get(append(path, internal.Field_nameTag))
941 p.printElementString(nameSi, w, indent, fld.GetName())
942 fmt.Fprint(w, "= ")
943
944 numSi := sourceInfo.Get(append(path, internal.Field_numberTag))
945 p.printElementString(numSi, w, indent, fmt.Sprintf("%d", fld.GetNumber()))
946
947 opts, err := p.extractOptions(fld, fld.GetOptions(), mf)
948 if err != nil {
949 if w.err == nil {
950 w.err = err
951 }
952 return
953 }
954
955 // we use negative values for "extras" keys so they can't collide
956 // with legit option tags
957
958 if !fld.GetFile().IsProto3() && fld.AsFieldDescriptorProto().DefaultValue != nil {
959 defVal := fld.GetDefaultValue()
960 if fld.GetEnumType() != nil {
961 defVal = fld.GetEnumType().FindValueByNumber(defVal.(int32))
962 }
963 opts[-internal.Field_defaultTag] = []option{{name: "default", val: defVal}}
964 }
965
966 jsn := fld.AsFieldDescriptorProto().GetJsonName()
967 if jsn != "" && jsn != internal.JsonName(fld.GetName()) {
968 opts[-internal.Field_jsonNameTag] = []option{{name: "json_name", val: jsn}}
969 }
970
971 elements := elementAddrs{dsc: fld, opts: opts}
972 elements.addrs = optionsAsElementAddrs(internal.Field_optionsTag, 0, opts)
973 p.sort(elements, sourceInfo, path)
974 p.printOptionElementsShort(elements, w, sourceInfo, path, indent)
975
976 fmt.Fprint(w, ";")
977 }
978 })
979}
980
981func shouldEmitLabel(fld *desc.FieldDescriptor) bool {
982 return !fld.IsMap() && fld.GetOneOf() == nil && (fld.GetLabel() != descriptor.FieldDescriptorProto_LABEL_OPTIONAL || !fld.GetFile().IsProto3())
983}
984
985func labelString(lbl descriptor.FieldDescriptorProto_Label) string {
986 switch lbl {
987 case descriptor.FieldDescriptorProto_LABEL_OPTIONAL:
988 return "optional"
989 case descriptor.FieldDescriptorProto_LABEL_REQUIRED:
990 return "required"
991 case descriptor.FieldDescriptorProto_LABEL_REPEATED:
992 return "repeated"
993 }
994 panic(fmt.Sprintf("invalid label: %v", lbl))
995}
996
997func isGroup(fld *desc.FieldDescriptor) bool {
998 return fld.GetType() == descriptor.FieldDescriptorProto_TYPE_GROUP
999}
1000
1001func (p *Printer) printOneOf(ood *desc.OneOfDescriptor, parentElements elementAddrs, startFieldIndex int, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, indent int, ooIndex int32) {
1002 oopath := append(parentPath, internal.Message_oneOfsTag, ooIndex)
1003 oosi := sourceInfo.Get(oopath)
1004 p.printElement(true, oosi, w, indent, func(w *writer) {
1005 p.indent(w, indent)
1006 fmt.Fprint(w, "oneof ")
1007 extNameSi := sourceInfo.Get(append(oopath, internal.OneOf_nameTag))
1008 p.printElementString(extNameSi, w, indent, ood.GetName())
1009 fmt.Fprintln(w, "{")
1010
1011 indent++
1012 opts, err := p.extractOptions(ood, ood.GetOptions(), mf)
1013 if err != nil {
1014 if w.err == nil {
1015 w.err = err
1016 }
1017 return
1018 }
1019
1020 elements := elementAddrs{dsc: ood, opts: opts}
1021 elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.OneOf_optionsTag, -1, opts)...)
1022
1023 count := len(ood.GetChoices())
1024 for idx := startFieldIndex; count > 0 && idx < len(parentElements.addrs); idx++ {
1025 el := parentElements.addrs[idx]
1026 if el.elementType != internal.Message_fieldsTag {
1027 continue
1028 }
1029 if parentElements.at(el).(*desc.FieldDescriptor).GetOneOf() == ood {
1030 // negative tag indicates that this element is actually a sibling, not a child
1031 elements.addrs = append(elements.addrs, elementAddr{elementType: -internal.Message_fieldsTag, elementIndex: el.elementIndex})
1032 count--
1033 }
1034 }
1035
Scott Baker4a35a702019-11-26 08:17:33 -08001036 // the fields are already sorted, but we have to re-sort in order to
1037 // interleave the options (in the event that we are using file location
1038 // order and the option locations are interleaved with the fields)
Zack Williamse940c7a2019-08-21 14:25:39 -07001039 p.sort(elements, sourceInfo, oopath)
Zack Williamse940c7a2019-08-21 14:25:39 -07001040 scope := ood.GetOwner().GetFullyQualifiedName()
1041
1042 for i, el := range elements.addrs {
1043 if i > 0 {
1044 p.newLine(w)
1045 }
1046
1047 switch d := elements.at(el).(type) {
1048 case []option:
1049 childPath := append(oopath, el.elementType, int32(el.elementIndex))
1050 p.printOptionsLong(d, w, sourceInfo, childPath, indent)
1051 case *desc.FieldDescriptor:
1052 childPath := append(parentPath, -el.elementType, int32(el.elementIndex))
1053 p.printField(d, mf, w, sourceInfo, childPath, scope, indent)
1054 }
1055 }
1056
1057 p.indent(w, indent-1)
1058 fmt.Fprintln(w, "}")
1059 })
1060}
1061
Scott Baker4a35a702019-11-26 08:17:33 -08001062func (p *Printer) printExtensions(exts *extensionDecl, allExts extensions, parentElements elementAddrs, startFieldIndex int, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, extTag int32, pkg, scope string, indent int) {
1063 path := append(parentPath, extTag)
1064 p.printLeadingComments(exts.sourceInfo, w, indent)
1065 p.indent(w, indent)
1066 fmt.Fprint(w, "extend ")
1067 extNameSi := sourceInfo.Get(append(path, 0, internal.Field_extendeeTag))
1068 p.printElementString(extNameSi, w, indent, p.qualifyName(pkg, scope, exts.extendee))
1069 fmt.Fprintln(w, "{")
1070
1071 count := len(exts.fields)
1072 first := true
1073 for idx := startFieldIndex; count > 0 && idx < len(parentElements.addrs); idx++ {
1074 el := parentElements.addrs[idx]
1075 if el.elementType != extTag {
1076 continue
1077 }
1078 fld := parentElements.at(el).(*desc.FieldDescriptor)
1079 if allExts[fld] == exts {
1080 if first {
1081 first = false
1082 } else {
1083 p.newLine(w)
1084 }
1085 childPath := append(path, int32(el.elementIndex))
1086 p.printField(fld, mf, w, sourceInfo, childPath, scope, indent+1)
1087 count--
1088 }
1089 }
1090
1091 p.indent(w, indent)
1092 fmt.Fprintln(w, "}")
1093 p.printTrailingComments(exts.sourceInfo, w, indent)
1094 if indent >= 0 && !w.newline {
1095 // if we're not printing inline but element did not have trailing newline, add one now
1096 fmt.Fprintln(w)
1097 }
1098}
1099
Zack Williamse940c7a2019-08-21 14:25:39 -07001100func (p *Printer) printExtensionRanges(parent *desc.MessageDescriptor, ranges []*descriptor.DescriptorProto_ExtensionRange, addrs []elementAddr, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, indent int) {
1101 p.indent(w, indent)
1102 fmt.Fprint(w, "extensions ")
1103
1104 var opts *descriptor.ExtensionRangeOptions
1105 var elPath []int32
1106 first := true
1107 for i, extr := range ranges {
1108 if first {
1109 first = false
1110 } else {
1111 fmt.Fprint(w, ", ")
1112 }
1113 opts = extr.Options
1114 el := addrs[i]
1115 elPath = append(parentPath, el.elementType, int32(el.elementIndex))
1116 si := sourceInfo.Get(elPath)
1117 p.printElement(true, si, w, inline(indent), func(w *writer) {
1118 if extr.GetStart() == extr.GetEnd()-1 {
1119 fmt.Fprintf(w, "%d ", extr.GetStart())
1120 } else if extr.GetEnd()-1 == internal.MaxTag {
1121 fmt.Fprintf(w, "%d to max ", extr.GetStart())
1122 } else {
1123 fmt.Fprintf(w, "%d to %d ", extr.GetStart(), extr.GetEnd()-1)
1124 }
1125 })
1126 }
1127 dsc := extensionRange{owner: parent, extRange: ranges[0]}
1128 p.printOptionsShort(dsc, opts, mf, internal.ExtensionRange_optionsTag, w, sourceInfo, elPath, indent)
1129
1130 fmt.Fprintln(w, ";")
1131}
1132
1133func (p *Printer) printReservedRanges(ranges []reservedRange, isEnum bool, addrs []elementAddr, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, indent int) {
1134 p.indent(w, indent)
1135 fmt.Fprint(w, "reserved ")
1136
1137 first := true
1138 for i, rr := range ranges {
1139 if first {
1140 first = false
1141 } else {
1142 fmt.Fprint(w, ", ")
1143 }
1144 el := addrs[i]
1145 si := sourceInfo.Get(append(parentPath, el.elementType, int32(el.elementIndex)))
1146 p.printElement(false, si, w, inline(indent), func(w *writer) {
1147 if rr.start == rr.end {
1148 fmt.Fprintf(w, "%d ", rr.start)
1149 } else if (rr.end == internal.MaxTag && !isEnum) ||
1150 (rr.end == math.MaxInt32 && isEnum) {
1151 fmt.Fprintf(w, "%d to max ", rr.start)
1152 } else {
1153 fmt.Fprintf(w, "%d to %d ", rr.start, rr.end)
1154 }
1155 })
1156 }
1157
1158 fmt.Fprintln(w, ";")
1159}
1160
1161func (p *Printer) printReservedNames(names []string, addrs []elementAddr, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, indent int) {
1162 p.indent(w, indent)
1163 fmt.Fprint(w, "reserved ")
1164
1165 first := true
1166 for i, name := range names {
1167 if first {
1168 first = false
1169 } else {
1170 fmt.Fprint(w, ", ")
1171 }
1172 el := addrs[i]
1173 si := sourceInfo.Get(append(parentPath, el.elementType, int32(el.elementIndex)))
1174 p.printElementString(si, w, indent, quotedString(name))
1175 }
1176
1177 fmt.Fprintln(w, ";")
1178}
1179
1180func (p *Printer) printEnum(ed *desc.EnumDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1181 si := sourceInfo.Get(path)
1182 p.printElement(true, si, w, indent, func(w *writer) {
1183 p.indent(w, indent)
1184
1185 fmt.Fprint(w, "enum ")
1186 nameSi := sourceInfo.Get(append(path, internal.Enum_nameTag))
1187 p.printElementString(nameSi, w, indent, ed.GetName())
1188 fmt.Fprintln(w, "{")
1189
1190 indent++
1191 opts, err := p.extractOptions(ed, ed.GetOptions(), mf)
1192 if err != nil {
1193 if w.err == nil {
1194 w.err = err
1195 }
1196 return
1197 }
1198
1199 skip := map[interface{}]bool{}
1200
1201 elements := elementAddrs{dsc: ed, opts: opts}
1202 elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.Enum_optionsTag, -1, opts)...)
1203 for i := range ed.GetValues() {
1204 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Enum_valuesTag, elementIndex: i})
1205 }
1206 for i := range ed.AsEnumDescriptorProto().GetReservedRange() {
1207 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Enum_reservedRangeTag, elementIndex: i})
1208 }
1209 for i := range ed.AsEnumDescriptorProto().GetReservedName() {
1210 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Enum_reservedNameTag, elementIndex: i})
1211 }
1212
1213 p.sort(elements, sourceInfo, path)
1214
1215 for i, el := range elements.addrs {
1216 d := elements.at(el)
1217
1218 // skip[d] will panic if d is a slice (which it could be for []option),
1219 // so just ignore it since we don't try to skip options
1220 if reflect.TypeOf(d).Kind() != reflect.Slice && skip[d] {
1221 // skip this element
1222 continue
1223 }
1224
1225 if i > 0 {
1226 p.newLine(w)
1227 }
1228
1229 childPath := append(path, el.elementType, int32(el.elementIndex))
1230
1231 switch d := d.(type) {
1232 case []option:
1233 p.printOptionsLong(d, w, sourceInfo, childPath, indent)
1234 case *desc.EnumValueDescriptor:
1235 p.printEnumValue(d, mf, w, sourceInfo, childPath, indent)
1236 case reservedRange:
1237 // collapse reserved ranges into a single "reserved" block
1238 ranges := []reservedRange{d}
1239 addrs := []elementAddr{el}
1240 for idx := i + 1; idx < len(elements.addrs); idx++ {
1241 elnext := elements.addrs[idx]
1242 if elnext.elementType != el.elementType {
1243 break
1244 }
1245 rr := elements.at(elnext).(reservedRange)
1246 ranges = append(ranges, rr)
1247 addrs = append(addrs, elnext)
1248 skip[rr] = true
1249 }
1250 p.printReservedRanges(ranges, true, addrs, w, sourceInfo, path, indent)
1251 case string: // reserved name
1252 // collapse reserved names into a single "reserved" block
1253 names := []string{d}
1254 addrs := []elementAddr{el}
1255 for idx := i + 1; idx < len(elements.addrs); idx++ {
1256 elnext := elements.addrs[idx]
1257 if elnext.elementType != el.elementType {
1258 break
1259 }
1260 rn := elements.at(elnext).(string)
1261 names = append(names, rn)
1262 addrs = append(addrs, elnext)
1263 skip[rn] = true
1264 }
1265 p.printReservedNames(names, addrs, w, sourceInfo, path, indent)
1266 }
1267 }
1268
1269 p.indent(w, indent-1)
1270 fmt.Fprintln(w, "}")
1271 })
1272}
1273
1274func (p *Printer) printEnumValue(evd *desc.EnumValueDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1275 si := sourceInfo.Get(path)
1276 p.printElement(true, si, w, indent, func(w *writer) {
1277 p.indent(w, indent)
1278
1279 nameSi := sourceInfo.Get(append(path, internal.EnumVal_nameTag))
1280 p.printElementString(nameSi, w, indent, evd.GetName())
1281 fmt.Fprint(w, "= ")
1282
1283 numSi := sourceInfo.Get(append(path, internal.EnumVal_numberTag))
1284 p.printElementString(numSi, w, indent, fmt.Sprintf("%d", evd.GetNumber()))
1285
1286 p.printOptionsShort(evd, evd.GetOptions(), mf, internal.EnumVal_optionsTag, w, sourceInfo, path, indent)
1287
1288 fmt.Fprint(w, ";")
1289 })
1290}
1291
1292func (p *Printer) printService(sd *desc.ServiceDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1293 si := sourceInfo.Get(path)
1294 p.printElement(true, si, w, indent, func(w *writer) {
1295 p.indent(w, indent)
1296
1297 fmt.Fprint(w, "service ")
1298 nameSi := sourceInfo.Get(append(path, internal.Service_nameTag))
1299 p.printElementString(nameSi, w, indent, sd.GetName())
1300 fmt.Fprintln(w, "{")
1301
1302 indent++
1303
1304 opts, err := p.extractOptions(sd, sd.GetOptions(), mf)
1305 if err != nil {
1306 if w.err == nil {
1307 w.err = err
1308 }
1309 return
1310 }
1311
1312 elements := elementAddrs{dsc: sd, opts: opts}
1313 elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.Service_optionsTag, -1, opts)...)
1314 for i := range sd.GetMethods() {
1315 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Service_methodsTag, elementIndex: i})
1316 }
1317
1318 p.sort(elements, sourceInfo, path)
1319
1320 for i, el := range elements.addrs {
1321 if i > 0 {
1322 p.newLine(w)
1323 }
1324
1325 childPath := append(path, el.elementType, int32(el.elementIndex))
1326
1327 switch d := elements.at(el).(type) {
1328 case []option:
1329 p.printOptionsLong(d, w, sourceInfo, childPath, indent)
1330 case *desc.MethodDescriptor:
1331 p.printMethod(d, mf, w, sourceInfo, childPath, indent)
1332 }
1333 }
1334
1335 p.indent(w, indent-1)
1336 fmt.Fprintln(w, "}")
1337 })
1338}
1339
1340func (p *Printer) printMethod(mtd *desc.MethodDescriptor, mf *dynamic.MessageFactory, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1341 si := sourceInfo.Get(path)
1342 pkg := mtd.GetFile().GetPackage()
1343 p.printElement(true, si, w, indent, func(w *writer) {
1344 p.indent(w, indent)
1345
1346 fmt.Fprint(w, "rpc ")
1347 nameSi := sourceInfo.Get(append(path, internal.Method_nameTag))
1348 p.printElementString(nameSi, w, indent, mtd.GetName())
1349
1350 fmt.Fprint(w, "( ")
1351 inSi := sourceInfo.Get(append(path, internal.Method_inputTag))
1352 inName := p.qualifyName(pkg, pkg, mtd.GetInputType().GetFullyQualifiedName())
1353 if mtd.IsClientStreaming() {
1354 inName = "stream " + inName
1355 }
1356 p.printElementString(inSi, w, indent, inName)
1357
1358 fmt.Fprint(w, ") returns ( ")
1359
1360 outSi := sourceInfo.Get(append(path, internal.Method_outputTag))
1361 outName := p.qualifyName(pkg, pkg, mtd.GetOutputType().GetFullyQualifiedName())
1362 if mtd.IsServerStreaming() {
1363 outName = "stream " + outName
1364 }
1365 p.printElementString(outSi, w, indent, outName)
1366 fmt.Fprint(w, ") ")
1367
1368 opts, err := p.extractOptions(mtd, mtd.GetOptions(), mf)
1369 if err != nil {
1370 if w.err == nil {
1371 w.err = err
1372 }
1373 return
1374 }
1375
1376 if len(opts) > 0 {
1377 fmt.Fprintln(w, "{")
1378 indent++
1379
1380 elements := elementAddrs{dsc: mtd, opts: opts}
1381 elements.addrs = optionsAsElementAddrs(internal.Method_optionsTag, 0, opts)
1382 p.sort(elements, sourceInfo, path)
1383 path = append(path, internal.Method_optionsTag)
1384
1385 for i, addr := range elements.addrs {
1386 if i > 0 {
1387 p.newLine(w)
1388 }
1389 o := elements.at(addr).([]option)
1390 p.printOptionsLong(o, w, sourceInfo, path, indent)
1391 }
1392
1393 p.indent(w, indent-1)
1394 fmt.Fprintln(w, "}")
1395 } else {
1396 fmt.Fprint(w, ";")
1397 }
1398 })
1399}
1400
1401func (p *Printer) printOptionsLong(opts []option, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1402 p.printOptions(opts, w, indent,
1403 func(i int32) *descriptor.SourceCodeInfo_Location {
1404 return sourceInfo.Get(append(path, i))
1405 },
1406 func(w *writer, indent int, opt option) {
1407 p.indent(w, indent)
1408 fmt.Fprint(w, "option ")
1409 p.printOption(opt.name, opt.val, w, indent)
1410 fmt.Fprint(w, ";")
1411 })
1412}
1413
1414func (p *Printer) printOptionsShort(dsc interface{}, optsMsg proto.Message, mf *dynamic.MessageFactory, optsTag int32, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1415 d, ok := dsc.(desc.Descriptor)
1416 if !ok {
1417 d = dsc.(extensionRange).owner
1418 }
1419 opts, err := p.extractOptions(d, optsMsg, mf)
1420 if err != nil {
1421 if w.err == nil {
1422 w.err = err
1423 }
1424 return
1425 }
1426
1427 elements := elementAddrs{dsc: dsc, opts: opts}
1428 elements.addrs = optionsAsElementAddrs(optsTag, 0, opts)
1429 p.sort(elements, sourceInfo, path)
1430 p.printOptionElementsShort(elements, w, sourceInfo, path, indent)
1431}
1432
1433func (p *Printer) printOptionElementsShort(addrs elementAddrs, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) {
1434 if len(addrs.addrs) == 0 {
1435 return
1436 }
1437 first := true
1438 fmt.Fprint(w, "[")
1439 for _, addr := range addrs.addrs {
1440 opts := addrs.at(addr).([]option)
1441 var childPath []int32
1442 if addr.elementIndex < 0 {
1443 // pseudo-option
1444 childPath = append(path, int32(-addr.elementIndex))
1445 } else {
1446 childPath = append(path, addr.elementType, int32(addr.elementIndex))
1447 }
1448 p.printOptions(opts, w, inline(indent),
1449 func(i int32) *descriptor.SourceCodeInfo_Location {
1450 p := childPath
1451 if addr.elementIndex >= 0 {
1452 p = append(p, i)
1453 }
1454 return sourceInfo.Get(p)
1455 },
1456 func(w *writer, indent int, opt option) {
1457 if first {
1458 first = false
1459 } else {
1460 fmt.Fprint(w, ", ")
1461 }
1462 p.printOption(opt.name, opt.val, w, indent)
1463 fmt.Fprint(w, " ") // trailing space
1464 })
1465 }
1466 fmt.Fprint(w, "]")
1467}
1468
1469func (p *Printer) printOptions(opts []option, w *writer, indent int, siFetch func(i int32) *descriptor.SourceCodeInfo_Location, fn func(w *writer, indent int, opt option)) {
1470 for i, opt := range opts {
1471 si := siFetch(int32(i))
1472 p.printElement(false, si, w, indent, func(w *writer) {
1473 fn(w, indent, opt)
1474 })
1475 }
1476}
1477
1478func inline(indent int) int {
1479 if indent < 0 {
1480 // already inlined
1481 return indent
1482 }
1483 // negative indent means inline; indent 2 stops further in case value wraps
1484 return -indent - 2
1485}
1486
1487func sortKeys(m map[interface{}]interface{}) []interface{} {
1488 res := make(sortedKeys, len(m))
1489 i := 0
1490 for k := range m {
1491 res[i] = k
1492 i++
1493 }
1494 sort.Sort(res)
1495 return ([]interface{})(res)
1496}
1497
1498type sortedKeys []interface{}
1499
1500func (k sortedKeys) Len() int {
1501 return len(k)
1502}
1503
1504func (k sortedKeys) Swap(i, j int) {
1505 k[i], k[j] = k[j], k[i]
1506}
1507
1508func (k sortedKeys) Less(i, j int) bool {
1509 switch i := k[i].(type) {
1510 case int32:
1511 return i < k[j].(int32)
1512 case uint32:
1513 return i < k[j].(uint32)
1514 case int64:
1515 return i < k[j].(int64)
1516 case uint64:
1517 return i < k[j].(uint64)
1518 case string:
1519 return i < k[j].(string)
1520 case bool:
1521 return !i && k[j].(bool)
1522 default:
1523 panic(fmt.Sprintf("invalid type for map key: %T", i))
1524 }
1525}
1526
1527func (p *Printer) printOption(name string, optVal interface{}, w *writer, indent int) {
1528 fmt.Fprintf(w, "%s = ", name)
1529
1530 switch optVal := optVal.(type) {
1531 case int32, uint32, int64, uint64:
1532 fmt.Fprintf(w, "%d", optVal)
1533 case float32, float64:
1534 fmt.Fprintf(w, "%f", optVal)
1535 case string:
1536 fmt.Fprintf(w, "%s", quotedString(optVal))
1537 case []byte:
1538 fmt.Fprintf(w, "%s", quotedString(string(optVal)))
1539 case bool:
1540 fmt.Fprintf(w, "%v", optVal)
1541 case ident:
1542 fmt.Fprintf(w, "%s", optVal)
1543 case *desc.EnumValueDescriptor:
1544 fmt.Fprintf(w, "%s", optVal.GetName())
1545 case proto.Message:
1546 // TODO: if value is too long, marshal to text format with indentation to
1547 // make output prettier (also requires correctly indenting subsequent lines)
1548
1549 // TODO: alternate approach so we can apply p.ForceFullyQualifiedNames
1550 // inside the resulting value?
1551
1552 fmt.Fprintf(w, "{ %s }", proto.CompactTextString(optVal))
1553 default:
1554 panic(fmt.Sprintf("unknown type of value %T for field %s", optVal, name))
1555 }
1556}
1557
1558type edgeKind int
1559
1560const (
1561 edgeKindOption edgeKind = iota
1562 edgeKindFile
1563 edgeKindMessage
1564 edgeKindField
1565 edgeKindOneOf
1566 edgeKindExtensionRange
1567 edgeKindEnum
1568 edgeKindEnumVal
1569 edgeKindService
1570 edgeKindMethod
1571)
1572
1573// edges in simple state machine for matching options paths
1574// whose prefix should be included in source info to handle
1575// the way options are printed (which cannot always include
1576// the full path from original source)
1577var edges = map[edgeKind]map[int32]edgeKind{
1578 edgeKindFile: {
1579 internal.File_optionsTag: edgeKindOption,
1580 internal.File_messagesTag: edgeKindMessage,
1581 internal.File_enumsTag: edgeKindEnum,
1582 internal.File_extensionsTag: edgeKindField,
1583 internal.File_servicesTag: edgeKindService,
1584 },
1585 edgeKindMessage: {
1586 internal.Message_optionsTag: edgeKindOption,
1587 internal.Message_fieldsTag: edgeKindField,
1588 internal.Message_oneOfsTag: edgeKindOneOf,
1589 internal.Message_nestedMessagesTag: edgeKindMessage,
1590 internal.Message_enumsTag: edgeKindEnum,
1591 internal.Message_extensionsTag: edgeKindField,
1592 internal.Message_extensionRangeTag: edgeKindExtensionRange,
1593 // TODO: reserved range tag
1594 },
1595 edgeKindField: {
1596 internal.Field_optionsTag: edgeKindOption,
1597 },
1598 edgeKindOneOf: {
1599 internal.OneOf_optionsTag: edgeKindOption,
1600 },
1601 edgeKindExtensionRange: {
1602 internal.ExtensionRange_optionsTag: edgeKindOption,
1603 },
1604 edgeKindEnum: {
1605 internal.Enum_optionsTag: edgeKindOption,
1606 internal.Enum_valuesTag: edgeKindEnumVal,
1607 },
1608 edgeKindEnumVal: {
1609 internal.EnumVal_optionsTag: edgeKindOption,
1610 },
1611 edgeKindService: {
1612 internal.Service_optionsTag: edgeKindOption,
1613 internal.Service_methodsTag: edgeKindMethod,
1614 },
1615 edgeKindMethod: {
1616 internal.Method_optionsTag: edgeKindOption,
1617 },
1618}
1619
Scott Baker4a35a702019-11-26 08:17:33 -08001620func extendOptionLocations(sc internal.SourceInfoMap, locs []*descriptor.SourceCodeInfo_Location) {
1621 // we iterate in the order that locations appear in descriptor
1622 // for determinism (if we ranged over the map, order and thus
1623 // potentially results are non-deterministic)
1624 for _, loc := range locs {
Zack Williamse940c7a2019-08-21 14:25:39 -07001625 allowed := edges[edgeKindFile]
1626 for i := 0; i+1 < len(loc.Path); i += 2 {
1627 nextKind, ok := allowed[loc.Path[i]]
1628 if !ok {
1629 break
1630 }
1631 if nextKind == edgeKindOption {
1632 // We've found an option entry. This could be arbitrarily
1633 // deep (for options that nested messages) or it could end
1634 // abruptly (for non-repeated fields). But we need a path
1635 // that is exactly the path-so-far plus two: the option tag
1636 // and an optional index for repeated option fields (zero
1637 // for non-repeated option fields). This is used for
1638 // querying source info when printing options.
1639 // for sorting elements
1640 newPath := make([]int32, i+3)
1641 copy(newPath, loc.Path)
1642 sc.PutIfAbsent(newPath, loc)
1643 // we do another path of path-so-far plus two, but with
1644 // explicit zero index -- just in case this actual path has
1645 // an extra path element, but it's not an index (e.g the
1646 // option field is not repeated, but the source info we are
1647 // looking at indicates a tag of a nested field)
1648 newPath[len(newPath)-1] = 0
1649 sc.PutIfAbsent(newPath, loc)
1650 // finally, we need the path-so-far plus one, just the option
1651 // tag, for sorting option groups
1652 newPath = newPath[:len(newPath)-1]
1653 sc.PutIfAbsent(newPath, loc)
1654
1655 break
1656 } else {
1657 allowed = edges[nextKind]
1658 }
1659 }
1660 }
1661}
1662
1663func (p *Printer) extractOptions(dsc desc.Descriptor, opts proto.Message, mf *dynamic.MessageFactory) (map[int32][]option, error) {
1664 md, err := desc.LoadMessageDescriptorForMessage(opts)
1665 if err != nil {
1666 return nil, err
1667 }
1668 dm := mf.NewDynamicMessage(md)
1669 if err = dm.ConvertFrom(opts); err != nil {
1670 return nil, fmt.Errorf("failed convert %s to dynamic message: %v", md.GetFullyQualifiedName(), err)
1671 }
1672
1673 pkg := dsc.GetFile().GetPackage()
1674 var scope string
1675 if _, ok := dsc.(*desc.FileDescriptor); ok {
1676 scope = pkg
1677 } else {
1678 scope = dsc.GetFullyQualifiedName()
1679 }
1680
1681 options := map[int32][]option{}
1682 var uninterpreted []interface{}
1683 for _, fldset := range [][]*desc.FieldDescriptor{md.GetFields(), mf.GetExtensionRegistry().AllExtensionsForType(md.GetFullyQualifiedName())} {
1684 for _, fld := range fldset {
1685 if dm.HasField(fld) {
1686 val := dm.GetField(fld)
1687 var opts []option
1688 var name string
1689 if fld.IsExtension() {
1690 name = fmt.Sprintf("(%s)", p.qualifyName(pkg, scope, fld.GetFullyQualifiedName()))
1691 } else {
1692 name = fld.GetName()
1693 }
1694 switch val := val.(type) {
1695 case []interface{}:
1696 if fld.GetNumber() == internal.UninterpretedOptionsTag {
1697 // we handle uninterpreted options differently
1698 uninterpreted = val
1699 continue
1700 }
1701
1702 for _, e := range val {
1703 if fld.GetType() == descriptor.FieldDescriptorProto_TYPE_ENUM {
1704 ev := fld.GetEnumType().FindValueByNumber(e.(int32))
1705 if ev == nil {
1706 // have to skip unknown enum values :(
1707 continue
1708 }
1709 e = ev
1710 }
1711 var name string
1712 if fld.IsExtension() {
1713 name = fmt.Sprintf("(%s)", p.qualifyName(pkg, scope, fld.GetFullyQualifiedName()))
1714 } else {
1715 name = fld.GetName()
1716 }
1717 opts = append(opts, option{name: name, val: e})
1718 }
1719 case map[interface{}]interface{}:
1720 for k := range sortKeys(val) {
1721 v := val[k]
1722 vf := fld.GetMapValueType()
1723 if vf.GetType() == descriptor.FieldDescriptorProto_TYPE_ENUM {
1724 ev := vf.GetEnumType().FindValueByNumber(v.(int32))
1725 if ev == nil {
1726 // have to skip unknown enum values :(
1727 continue
1728 }
1729 v = ev
1730 }
1731 entry := mf.NewDynamicMessage(fld.GetMessageType())
1732 entry.SetFieldByNumber(1, k)
1733 entry.SetFieldByNumber(2, v)
1734 opts = append(opts, option{name: name, val: entry})
1735 }
1736 default:
1737 if fld.GetType() == descriptor.FieldDescriptorProto_TYPE_ENUM {
1738 ev := fld.GetEnumType().FindValueByNumber(val.(int32))
1739 if ev == nil {
1740 // have to skip unknown enum values :(
1741 continue
1742 }
1743 val = ev
1744 }
1745 opts = append(opts, option{name: name, val: val})
1746 }
1747 if len(opts) > 0 {
1748 options[fld.GetNumber()] = opts
1749 }
1750 }
1751 }
1752 }
1753
1754 // if there are uninterpreted options, add those too
1755 if len(uninterpreted) > 0 {
1756 opts := make([]option, len(uninterpreted))
1757 for i, u := range uninterpreted {
1758 var unint *descriptor.UninterpretedOption
1759 if un, ok := u.(*descriptor.UninterpretedOption); ok {
1760 unint = un
1761 } else {
1762 dm := u.(*dynamic.Message)
1763 unint = &descriptor.UninterpretedOption{}
1764 if err := dm.ConvertTo(unint); err != nil {
1765 return nil, err
1766 }
1767 }
1768
1769 var buf bytes.Buffer
1770 for ni, n := range unint.Name {
1771 if ni > 0 {
1772 buf.WriteByte('.')
1773 }
1774 if n.GetIsExtension() {
1775 fmt.Fprintf(&buf, "(%s)", n.GetNamePart())
1776 } else {
1777 buf.WriteString(n.GetNamePart())
1778 }
1779 }
1780
1781 var v interface{}
1782 switch {
1783 case unint.IdentifierValue != nil:
1784 v = ident(unint.GetIdentifierValue())
1785 case unint.StringValue != nil:
1786 v = string(unint.GetStringValue())
1787 case unint.DoubleValue != nil:
1788 v = unint.GetDoubleValue()
1789 case unint.PositiveIntValue != nil:
1790 v = unint.GetPositiveIntValue()
1791 case unint.NegativeIntValue != nil:
1792 v = unint.GetNegativeIntValue()
1793 case unint.AggregateValue != nil:
1794 v = ident(unint.GetAggregateValue())
1795 }
1796
1797 opts[i] = option{name: buf.String(), val: v}
1798 }
1799 options[internal.UninterpretedOptionsTag] = opts
1800 }
1801
1802 return options, nil
1803}
1804
1805func optionsAsElementAddrs(optionsTag int32, order int, opts map[int32][]option) []elementAddr {
1806 var optAddrs []elementAddr
1807 for tag := range opts {
1808 optAddrs = append(optAddrs, elementAddr{elementType: optionsTag, elementIndex: int(tag), order: order})
1809 }
1810 sort.Sort(optionsByName{addrs: optAddrs, opts: opts})
1811 return optAddrs
1812}
1813
1814// quotedString implements the text format for string literals for protocol
1815// buffers. This form is also acceptable for string literals in option values
1816// by the protocol buffer compiler, protoc.
1817func quotedString(s string) string {
1818 var b bytes.Buffer
1819 // use WriteByte here to get any needed indent
1820 b.WriteByte('"')
1821 // Loop over the bytes, not the runes.
1822 for i := 0; i < len(s); i++ {
1823 // Divergence from C++: we don't escape apostrophes.
1824 // There's no need to escape them, and the C++ parser
1825 // copes with a naked apostrophe.
1826 switch c := s[i]; c {
1827 case '\n':
1828 b.WriteString("\\n")
1829 case '\r':
1830 b.WriteString("\\r")
1831 case '\t':
1832 b.WriteString("\\t")
1833 case '"':
1834 b.WriteString("\\")
1835 case '\\':
1836 b.WriteString("\\\\")
1837 default:
1838 if c >= 0x20 && c < 0x7f {
1839 b.WriteByte(c)
1840 } else {
1841 fmt.Fprintf(&b, "\\%03o", c)
1842 }
1843 }
1844 }
1845 b.WriteByte('"')
1846
1847 return b.String()
1848}
1849
1850type elementAddr struct {
1851 elementType int32
1852 elementIndex int
1853 order int
1854}
1855
1856type elementAddrs struct {
1857 addrs []elementAddr
1858 dsc interface{}
1859 opts map[int32][]option
1860}
1861
1862func (a elementAddrs) Len() int {
1863 return len(a.addrs)
1864}
1865
1866func (a elementAddrs) Less(i, j int) bool {
1867 // explicit order is considered first
1868 if a.addrs[i].order < a.addrs[j].order {
1869 return true
1870 } else if a.addrs[i].order > a.addrs[j].order {
1871 return false
1872 }
1873 // if order is equal, sort by element type
1874 if a.addrs[i].elementType < a.addrs[j].elementType {
1875 return true
1876 } else if a.addrs[i].elementType > a.addrs[j].elementType {
1877 return false
1878 }
1879
1880 di := a.at(a.addrs[i])
1881 dj := a.at(a.addrs[j])
1882
1883 switch vi := di.(type) {
1884 case *desc.FieldDescriptor:
1885 // fields are ordered by tag number
1886 vj := dj.(*desc.FieldDescriptor)
1887 // regular fields before extensions; extensions grouped by extendee
1888 if !vi.IsExtension() && vj.IsExtension() {
1889 return true
1890 } else if vi.IsExtension() && !vj.IsExtension() {
1891 return false
1892 } else if vi.IsExtension() && vj.IsExtension() {
1893 if vi.GetOwner() != vj.GetOwner() {
1894 return vi.GetOwner().GetFullyQualifiedName() < vj.GetOwner().GetFullyQualifiedName()
1895 }
1896 }
1897 return vi.GetNumber() < vj.GetNumber()
1898
1899 case *desc.EnumValueDescriptor:
1900 // enum values ordered by number then name
1901 vj := dj.(*desc.EnumValueDescriptor)
1902 if vi.GetNumber() == vj.GetNumber() {
1903 return vi.GetName() < vj.GetName()
1904 }
1905 return vi.GetNumber() < vj.GetNumber()
1906
1907 case *descriptor.DescriptorProto_ExtensionRange:
1908 // extension ranges ordered by tag
1909 return vi.GetStart() < dj.(*descriptor.DescriptorProto_ExtensionRange).GetStart()
1910
1911 case reservedRange:
1912 // reserved ranges ordered by tag, too
1913 return vi.start < dj.(reservedRange).start
1914
1915 case string:
1916 // reserved names lexically sorted
1917 return vi < dj.(string)
1918
1919 case pkg:
1920 // reserved names lexically sorted
1921 return vi < dj.(pkg)
1922
1923 case imp:
1924 // reserved names lexically sorted
1925 return vi < dj.(imp)
1926
1927 case []option:
1928 // options sorted by name, extensions last
1929 return optionLess(vi, dj.([]option))
1930
1931 default:
1932 // all other descriptors ordered by name
1933 return di.(desc.Descriptor).GetName() < dj.(desc.Descriptor).GetName()
1934 }
1935}
1936
1937func (a elementAddrs) Swap(i, j int) {
1938 a.addrs[i], a.addrs[j] = a.addrs[j], a.addrs[i]
1939}
1940
1941func (a elementAddrs) at(addr elementAddr) interface{} {
1942 switch dsc := a.dsc.(type) {
1943 case *desc.FileDescriptor:
1944 switch addr.elementType {
1945 case internal.File_packageTag:
1946 return pkg(dsc.GetPackage())
1947 case internal.File_dependencyTag:
1948 return imp(dsc.AsFileDescriptorProto().GetDependency()[addr.elementIndex])
1949 case internal.File_optionsTag:
1950 return a.opts[int32(addr.elementIndex)]
1951 case internal.File_messagesTag:
1952 return dsc.GetMessageTypes()[addr.elementIndex]
1953 case internal.File_enumsTag:
1954 return dsc.GetEnumTypes()[addr.elementIndex]
1955 case internal.File_servicesTag:
1956 return dsc.GetServices()[addr.elementIndex]
1957 case internal.File_extensionsTag:
1958 return dsc.GetExtensions()[addr.elementIndex]
1959 }
1960 case *desc.MessageDescriptor:
1961 switch addr.elementType {
1962 case internal.Message_optionsTag:
1963 return a.opts[int32(addr.elementIndex)]
1964 case internal.Message_fieldsTag:
1965 return dsc.GetFields()[addr.elementIndex]
1966 case internal.Message_nestedMessagesTag:
1967 return dsc.GetNestedMessageTypes()[addr.elementIndex]
1968 case internal.Message_enumsTag:
1969 return dsc.GetNestedEnumTypes()[addr.elementIndex]
1970 case internal.Message_extensionsTag:
1971 return dsc.GetNestedExtensions()[addr.elementIndex]
1972 case internal.Message_extensionRangeTag:
1973 return dsc.AsDescriptorProto().GetExtensionRange()[addr.elementIndex]
1974 case internal.Message_reservedRangeTag:
1975 rng := dsc.AsDescriptorProto().GetReservedRange()[addr.elementIndex]
1976 return reservedRange{start: rng.GetStart(), end: rng.GetEnd() - 1}
1977 case internal.Message_reservedNameTag:
1978 return dsc.AsDescriptorProto().GetReservedName()[addr.elementIndex]
1979 }
1980 case *desc.FieldDescriptor:
1981 if addr.elementType == internal.Field_optionsTag {
1982 return a.opts[int32(addr.elementIndex)]
1983 }
1984 case *desc.OneOfDescriptor:
1985 switch addr.elementType {
1986 case internal.OneOf_optionsTag:
1987 return a.opts[int32(addr.elementIndex)]
1988 case -internal.Message_fieldsTag:
1989 return dsc.GetOwner().GetFields()[addr.elementIndex]
1990 }
1991 case *desc.EnumDescriptor:
1992 switch addr.elementType {
1993 case internal.Enum_optionsTag:
1994 return a.opts[int32(addr.elementIndex)]
1995 case internal.Enum_valuesTag:
1996 return dsc.GetValues()[addr.elementIndex]
1997 case internal.Enum_reservedRangeTag:
1998 rng := dsc.AsEnumDescriptorProto().GetReservedRange()[addr.elementIndex]
1999 return reservedRange{start: rng.GetStart(), end: rng.GetEnd()}
2000 case internal.Enum_reservedNameTag:
2001 return dsc.AsEnumDescriptorProto().GetReservedName()[addr.elementIndex]
2002 }
2003 case *desc.EnumValueDescriptor:
2004 if addr.elementType == internal.EnumVal_optionsTag {
2005 return a.opts[int32(addr.elementIndex)]
2006 }
2007 case *desc.ServiceDescriptor:
2008 switch addr.elementType {
2009 case internal.Service_optionsTag:
2010 return a.opts[int32(addr.elementIndex)]
2011 case internal.Service_methodsTag:
2012 return dsc.GetMethods()[addr.elementIndex]
2013 }
2014 case *desc.MethodDescriptor:
2015 if addr.elementType == internal.Method_optionsTag {
2016 return a.opts[int32(addr.elementIndex)]
2017 }
2018 case extensionRange:
2019 if addr.elementType == internal.ExtensionRange_optionsTag {
2020 return a.opts[int32(addr.elementIndex)]
2021 }
2022 }
2023
2024 panic(fmt.Sprintf("location for unknown field %d of %T", addr.elementType, a.dsc))
2025}
2026
2027type extensionRange struct {
2028 owner *desc.MessageDescriptor
2029 extRange *descriptor.DescriptorProto_ExtensionRange
2030}
2031
2032type elementSrcOrder struct {
2033 elementAddrs
2034 sourceInfo internal.SourceInfoMap
2035 prefix []int32
2036}
2037
2038func (a elementSrcOrder) Less(i, j int) bool {
2039 ti := a.addrs[i].elementType
2040 ei := a.addrs[i].elementIndex
2041
2042 tj := a.addrs[j].elementType
2043 ej := a.addrs[j].elementIndex
2044
2045 var si, sj *descriptor.SourceCodeInfo_Location
2046 if ei < 0 {
2047 si = a.sourceInfo.Get(append(a.prefix, -int32(ei)))
2048 } else if ti < 0 {
2049 p := make([]int32, len(a.prefix)-2)
2050 copy(p, a.prefix)
2051 si = a.sourceInfo.Get(append(p, ti, int32(ei)))
2052 } else {
2053 si = a.sourceInfo.Get(append(a.prefix, ti, int32(ei)))
2054 }
2055 if ej < 0 {
2056 sj = a.sourceInfo.Get(append(a.prefix, -int32(ej)))
2057 } else if tj < 0 {
2058 p := make([]int32, len(a.prefix)-2)
2059 copy(p, a.prefix)
2060 sj = a.sourceInfo.Get(append(p, tj, int32(ej)))
2061 } else {
2062 sj = a.sourceInfo.Get(append(a.prefix, tj, int32(ej)))
2063 }
2064
2065 if (si == nil) != (sj == nil) {
2066 // generally, we put unknown elements after known ones;
Scott Baker4a35a702019-11-26 08:17:33 -08002067 // except package, imports, and option elements go first
Zack Williamse940c7a2019-08-21 14:25:39 -07002068
2069 // i will be unknown and j will be known
2070 swapped := false
2071 if si != nil {
2072 si, sj = sj, si
Scott Baker4a35a702019-11-26 08:17:33 -08002073 ti, tj = tj, ti
Zack Williamse940c7a2019-08-21 14:25:39 -07002074 swapped = true
2075 }
2076 switch a.dsc.(type) {
2077 case *desc.FileDescriptor:
Scott Baker4a35a702019-11-26 08:17:33 -08002078 // NB: These comparisons are *trying* to get things ordered so that
2079 // 1) If the package element has no source info, it appears _first_.
2080 // 2) If any import element has no source info, it appears _after_
2081 // the package element but _before_ any other element.
2082 // 3) If any option element has no source info, it appears _after_
2083 // the package and import elements but _before_ any other element.
2084 // If the package, imports, and options are all missing source info,
2085 // this will sort them all to the top in expected order. But if they
2086 // are mixed (some _do_ have source info, some do not), and elements
2087 // with source info have spans that positions them _after_ other
2088 // elements in the file, then this Less function will be unstable
2089 // since the above dual objectives for imports and options ("before
2090 // this but after that") may be in conflict with one another. This
2091 // should not cause any problems, other than elements being possibly
2092 // sorted in a confusing order.
2093 //
2094 // Well-formed descriptors should instead have consistent source
2095 // info: either all elements have source info or none do. So this
2096 // should not be an issue in practice.
2097 if ti == internal.File_packageTag {
2098 return !swapped
2099 }
2100 if ti == internal.File_dependencyTag {
2101 if tj == internal.File_packageTag {
2102 // imports will come *after* package
2103 return swapped
2104 }
2105 return !swapped
2106 }
2107 if ti == internal.File_optionsTag {
2108 if tj == internal.File_packageTag || tj == internal.File_dependencyTag {
2109 // options will come *after* package and imports
2110 return swapped
2111 }
Zack Williamse940c7a2019-08-21 14:25:39 -07002112 return !swapped
2113 }
2114 case *desc.MessageDescriptor:
2115 if ti == internal.Message_optionsTag {
2116 return !swapped
2117 }
2118 case *desc.EnumDescriptor:
2119 if ti == internal.Enum_optionsTag {
2120 return !swapped
2121 }
2122 case *desc.ServiceDescriptor:
2123 if ti == internal.Service_optionsTag {
2124 return !swapped
2125 }
2126 }
2127 return swapped
2128
2129 } else if si == nil || sj == nil {
2130 // let stable sort keep unknown elements in same relative order
2131 return false
2132 }
2133
2134 for idx := 0; idx < len(sj.Span); idx++ {
2135 if idx >= len(si.Span) {
2136 return true
2137 }
2138 if si.Span[idx] < sj.Span[idx] {
2139 return true
2140 }
2141 if si.Span[idx] > sj.Span[idx] {
2142 return false
2143 }
2144 }
2145 return false
2146}
2147
2148type optionsByName struct {
2149 addrs []elementAddr
2150 opts map[int32][]option
2151}
2152
2153func (o optionsByName) Len() int {
2154 return len(o.addrs)
2155}
2156
2157func (o optionsByName) Less(i, j int) bool {
2158 oi := o.opts[int32(o.addrs[i].elementIndex)]
2159 oj := o.opts[int32(o.addrs[j].elementIndex)]
2160 return optionLess(oi, oj)
2161}
2162
2163func optionLess(i, j []option) bool {
2164 ni := i[0].name
2165 nj := j[0].name
2166 if ni[0] != '(' && nj[0] == '(' {
2167 return true
2168 } else if ni[0] == '(' && nj[0] != '(' {
2169 return false
2170 }
2171 return ni < nj
2172}
2173
2174func (o optionsByName) Swap(i, j int) {
2175 o.addrs[i], o.addrs[j] = o.addrs[j], o.addrs[i]
2176}
2177
2178func (p *Printer) printElement(isDecriptor bool, si *descriptor.SourceCodeInfo_Location, w *writer, indent int, el func(*writer)) {
2179 includeComments := isDecriptor || p.includeCommentType(CommentsTokens)
2180
2181 if includeComments && si != nil {
2182 p.printLeadingComments(si, w, indent)
2183 }
2184 el(w)
2185 if includeComments && si != nil {
2186 p.printTrailingComments(si, w, indent)
2187 }
2188 if indent >= 0 && !w.newline {
2189 // if we're not printing inline but element did not have trailing newline, add one now
2190 fmt.Fprintln(w)
2191 }
2192}
2193
2194func (p *Printer) printElementString(si *descriptor.SourceCodeInfo_Location, w *writer, indent int, str string) {
2195 p.printElement(false, si, w, inline(indent), func(w *writer) {
2196 fmt.Fprintf(w, "%s ", str)
2197 })
2198}
2199
2200func (p *Printer) includeCommentType(c CommentType) bool {
2201 return (p.OmitComments & c) == 0
2202}
2203
2204func (p *Printer) printLeadingComments(si *descriptor.SourceCodeInfo_Location, w *writer, indent int) bool {
2205 endsInNewLine := false
2206
2207 if p.includeCommentType(CommentsDetached) {
2208 for _, c := range si.GetLeadingDetachedComments() {
2209 if p.printComment(c, w, indent, true) {
2210 // if comment ended in newline, add another newline to separate
2211 // this comment from the next
2212 p.newLine(w)
2213 endsInNewLine = true
2214 } else if indent < 0 {
2215 // comment did not end in newline and we are trying to inline?
2216 // just add a space to separate this comment from what follows
2217 fmt.Fprint(w, " ")
2218 endsInNewLine = false
2219 } else {
2220 // comment did not end in newline and we are *not* trying to inline?
2221 // add newline to end of comment and add another to separate this
2222 // comment from what follows
2223 fmt.Fprintln(w) // needed to end comment, regardless of p.Compact
2224 p.newLine(w)
2225 endsInNewLine = true
2226 }
2227 }
2228 }
2229
2230 if p.includeCommentType(CommentsLeading) && si.GetLeadingComments() != "" {
2231 endsInNewLine = p.printComment(si.GetLeadingComments(), w, indent, true)
2232 if !endsInNewLine {
2233 if indent >= 0 {
2234 // leading comment didn't end with newline but needs one
2235 // (because we're *not* inlining)
2236 fmt.Fprintln(w) // needed to end comment, regardless of p.Compact
2237 endsInNewLine = true
2238 } else {
2239 // space between comment and following element when inlined
2240 fmt.Fprint(w, " ")
2241 }
2242 }
2243 }
2244
2245 return endsInNewLine
2246}
2247
2248func (p *Printer) printTrailingComments(si *descriptor.SourceCodeInfo_Location, w *writer, indent int) {
2249 if p.includeCommentType(CommentsTrailing) && si.GetTrailingComments() != "" {
2250 if !p.printComment(si.GetTrailingComments(), w, indent, p.TrailingCommentsOnSeparateLine) && indent >= 0 {
2251 // trailing comment didn't end with newline but needs one
2252 // (because we're *not* inlining)
2253 fmt.Fprintln(w) // needed to end comment, regardless of p.Compact
2254 } else if indent < 0 {
2255 fmt.Fprint(w, " ")
2256 }
2257 }
2258}
2259
2260func (p *Printer) printComment(comments string, w *writer, indent int, forceNextLine bool) bool {
2261 if comments == "" {
2262 return false
2263 }
2264
2265 var multiLine bool
2266 if indent < 0 {
2267 // use multi-line style when inlining
2268 multiLine = true
2269 } else {
2270 multiLine = p.PreferMultiLineStyleComments
2271 }
2272 if multiLine && strings.Contains(comments, "*/") {
2273 // can't emit '*/' in a multi-line style comment
2274 multiLine = false
2275 }
2276
2277 lines := strings.Split(comments, "\n")
2278
2279 // first, remove leading and trailing blank lines
2280 if lines[0] == "" {
2281 lines = lines[1:]
2282 }
2283 if lines[len(lines)-1] == "" {
2284 lines = lines[:len(lines)-1]
2285 }
2286 if len(lines) == 0 {
2287 return false
2288 }
2289
2290 if indent >= 0 && !w.newline {
2291 // last element did not have trailing newline, so we
2292 // either need to tack on newline or, if comment is
2293 // just one line, inline it on the end
2294 if forceNextLine || len(lines) > 1 {
2295 fmt.Fprintln(w)
2296 } else {
2297 if !w.space {
2298 fmt.Fprint(w, " ")
2299 }
2300 indent = inline(indent)
2301 }
2302 }
2303
2304 if len(lines) == 1 && multiLine {
2305 p.indent(w, indent)
2306 line := lines[0]
2307 if line[0] == ' ' && line[len(line)-1] != ' ' {
2308 // add trailing space for symmetry
2309 line += " "
2310 }
2311 fmt.Fprintf(w, "/*%s*/", line)
2312 if indent >= 0 {
2313 fmt.Fprintln(w)
2314 return true
2315 }
2316 return false
2317 }
2318
2319 if multiLine {
2320 // multi-line style comments that actually span multiple lines
2321 // get a blank line before and after so that comment renders nicely
2322 lines = append(lines, "", "")
2323 copy(lines[1:], lines)
2324 lines[0] = ""
2325 }
2326
2327 for i, l := range lines {
2328 p.maybeIndent(w, indent, i > 0)
2329 if multiLine {
2330 if i == 0 {
2331 // first line
2332 fmt.Fprintf(w, "/*%s\n", strings.TrimRight(l, " \t"))
2333 } else if i == len(lines)-1 {
2334 // last line
2335 if l == "" {
2336 fmt.Fprint(w, " */")
2337 } else {
2338 fmt.Fprintf(w, " *%s*/", l)
2339 }
2340 if indent >= 0 {
2341 fmt.Fprintln(w)
2342 }
2343 } else {
2344 fmt.Fprintf(w, " *%s\n", strings.TrimRight(l, " \t"))
2345 }
2346 } else {
2347 fmt.Fprintf(w, "//%s\n", strings.TrimRight(l, " \t"))
2348 }
2349 }
2350
2351 // single-line comments always end in newline; multi-line comments only
2352 // end in newline for non-negative (e.g. non-inlined) indentation
2353 return !multiLine || indent >= 0
2354}
2355
2356func (p *Printer) indent(w io.Writer, indent int) {
2357 for i := 0; i < indent; i++ {
2358 fmt.Fprint(w, p.Indent)
2359 }
2360}
2361
2362func (p *Printer) maybeIndent(w io.Writer, indent int, requireIndent bool) {
2363 if indent < 0 && requireIndent {
2364 p.indent(w, -indent)
2365 } else {
2366 p.indent(w, indent)
2367 }
2368}
2369
2370type writer struct {
2371 io.Writer
2372 err error
2373 space bool
2374 newline bool
2375}
2376
2377func newWriter(w io.Writer) *writer {
2378 return &writer{Writer: w, newline: true}
2379}
2380
2381func (w *writer) Write(p []byte) (int, error) {
2382 if len(p) == 0 {
2383 return 0, nil
2384 }
2385
2386 w.newline = false
2387
2388 if w.space {
2389 // skip any trailing space if the following
2390 // character is semicolon, comma, or close bracket
2391 if p[0] != ';' && p[0] != ',' && p[0] != ']' {
2392 _, err := w.Writer.Write([]byte{' '})
2393 if err != nil {
2394 w.err = err
2395 return 0, err
2396 }
2397 }
2398 w.space = false
2399 }
2400
2401 if p[len(p)-1] == ' ' {
2402 w.space = true
2403 p = p[:len(p)-1]
2404 }
2405 if len(p) > 0 && p[len(p)-1] == '\n' {
2406 w.newline = true
2407 }
2408
2409 num, err := w.Writer.Write(p)
2410 if err != nil {
2411 w.err = err
2412 } else if w.space {
2413 // pretend space was written
2414 num++
2415 }
2416 return num, err
2417}