|  | // Copyright 2019 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package internal_gengo | 
|  |  | 
|  | import ( | 
|  | "unicode" | 
|  | "unicode/utf8" | 
|  |  | 
|  | "google.golang.org/protobuf/compiler/protogen" | 
|  | "google.golang.org/protobuf/internal/encoding/wire" | 
|  |  | 
|  | "google.golang.org/protobuf/types/descriptorpb" | 
|  | ) | 
|  |  | 
|  | type fileInfo struct { | 
|  | *protogen.File | 
|  |  | 
|  | allEnums      []*enumInfo | 
|  | allMessages   []*messageInfo | 
|  | allExtensions []*extensionInfo | 
|  |  | 
|  | allEnumsByPtr         map[*enumInfo]int    // value is index into allEnums | 
|  | allMessagesByPtr      map[*messageInfo]int // value is index into allMessages | 
|  | allMessageFieldsByPtr map[*messageInfo]*structFields | 
|  |  | 
|  | // needRawDesc specifies whether the generator should emit logic to provide | 
|  | // the legacy raw descriptor in GZIP'd form. | 
|  | // This is updated by enum and message generation logic as necessary, | 
|  | // and checked at the end of file generation. | 
|  | needRawDesc bool | 
|  | } | 
|  |  | 
|  | type structFields struct { | 
|  | count      int | 
|  | unexported map[int]string | 
|  | } | 
|  |  | 
|  | func (sf *structFields) append(name string) { | 
|  | if r, _ := utf8.DecodeRuneInString(name); !unicode.IsUpper(r) { | 
|  | if sf.unexported == nil { | 
|  | sf.unexported = make(map[int]string) | 
|  | } | 
|  | sf.unexported[sf.count] = name | 
|  | } | 
|  | sf.count++ | 
|  | } | 
|  |  | 
|  | func newFileInfo(file *protogen.File) *fileInfo { | 
|  | f := &fileInfo{File: file} | 
|  |  | 
|  | // Collect all enums, messages, and extensions in "flattened ordering". | 
|  | // See filetype.TypeBuilder. | 
|  | var walkMessages func([]*protogen.Message, func(*protogen.Message)) | 
|  | walkMessages = func(messages []*protogen.Message, f func(*protogen.Message)) { | 
|  | for _, m := range messages { | 
|  | f(m) | 
|  | walkMessages(m.Messages, f) | 
|  | } | 
|  | } | 
|  | initEnumInfos := func(enums []*protogen.Enum) { | 
|  | for _, enum := range enums { | 
|  | f.allEnums = append(f.allEnums, newEnumInfo(f, enum)) | 
|  | } | 
|  | } | 
|  | initMessageInfos := func(messages []*protogen.Message) { | 
|  | for _, message := range messages { | 
|  | f.allMessages = append(f.allMessages, newMessageInfo(f, message)) | 
|  | } | 
|  | } | 
|  | initExtensionInfos := func(extensions []*protogen.Extension) { | 
|  | for _, extension := range extensions { | 
|  | f.allExtensions = append(f.allExtensions, newExtensionInfo(f, extension)) | 
|  | } | 
|  | } | 
|  | initEnumInfos(f.Enums) | 
|  | initMessageInfos(f.Messages) | 
|  | initExtensionInfos(f.Extensions) | 
|  | walkMessages(f.Messages, func(m *protogen.Message) { | 
|  | initEnumInfos(m.Enums) | 
|  | initMessageInfos(m.Messages) | 
|  | initExtensionInfos(m.Extensions) | 
|  | }) | 
|  |  | 
|  | // Derive a reverse mapping of enum and message pointers to their index | 
|  | // in allEnums and allMessages. | 
|  | if len(f.allEnums) > 0 { | 
|  | f.allEnumsByPtr = make(map[*enumInfo]int) | 
|  | for i, e := range f.allEnums { | 
|  | f.allEnumsByPtr[e] = i | 
|  | } | 
|  | } | 
|  | if len(f.allMessages) > 0 { | 
|  | f.allMessagesByPtr = make(map[*messageInfo]int) | 
|  | f.allMessageFieldsByPtr = make(map[*messageInfo]*structFields) | 
|  | for i, m := range f.allMessages { | 
|  | f.allMessagesByPtr[m] = i | 
|  | f.allMessageFieldsByPtr[m] = new(structFields) | 
|  | } | 
|  | } | 
|  |  | 
|  | return f | 
|  | } | 
|  |  | 
|  | type enumInfo struct { | 
|  | *protogen.Enum | 
|  |  | 
|  | genJSONMethod    bool | 
|  | genRawDescMethod bool | 
|  | } | 
|  |  | 
|  | func newEnumInfo(f *fileInfo, enum *protogen.Enum) *enumInfo { | 
|  | e := &enumInfo{Enum: enum} | 
|  | e.genJSONMethod = true | 
|  | e.genRawDescMethod = true | 
|  | return e | 
|  | } | 
|  |  | 
|  | type messageInfo struct { | 
|  | *protogen.Message | 
|  |  | 
|  | genRawDescMethod  bool | 
|  | genExtRangeMethod bool | 
|  |  | 
|  | isTracked bool | 
|  | hasWeak   bool | 
|  | } | 
|  |  | 
|  | func newMessageInfo(f *fileInfo, message *protogen.Message) *messageInfo { | 
|  | m := &messageInfo{Message: message} | 
|  | m.genRawDescMethod = true | 
|  | m.genExtRangeMethod = true | 
|  | m.isTracked = isTrackedMessage(m) | 
|  | for _, field := range m.Fields { | 
|  | m.hasWeak = m.hasWeak || field.Desc.IsWeak() | 
|  | } | 
|  | return m | 
|  | } | 
|  |  | 
|  | // isTrackedMessage reports whether field tracking is enabled on the message. | 
|  | func isTrackedMessage(m *messageInfo) (tracked bool) { | 
|  | const trackFieldUse_fieldNumber = 37383685 | 
|  |  | 
|  | // Decode the option from unknown fields to avoid a dependency on the | 
|  | // annotation proto from protoc-gen-go. | 
|  | b := m.Desc.Options().(*descriptorpb.MessageOptions).ProtoReflect().GetUnknown() | 
|  | for len(b) > 0 { | 
|  | num, typ, n := wire.ConsumeTag(b) | 
|  | b = b[n:] | 
|  | if num == trackFieldUse_fieldNumber && typ == wire.VarintType { | 
|  | v, _ := wire.ConsumeVarint(b) | 
|  | tracked = wire.DecodeBool(v) | 
|  | } | 
|  | m := wire.ConsumeFieldValue(num, typ, b) | 
|  | b = b[m:] | 
|  | } | 
|  | return tracked | 
|  | } | 
|  |  | 
|  | type extensionInfo struct { | 
|  | *protogen.Extension | 
|  | } | 
|  |  | 
|  | func newExtensionInfo(f *fileInfo, extension *protogen.Extension) *extensionInfo { | 
|  | x := &extensionInfo{Extension: extension} | 
|  | return x | 
|  | } |