blob: 369df13da27476061ab7f80c722cffd1942107ef [file] [log] [blame]
// 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/encoding/protowire"
"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 := protowire.ConsumeTag(b)
b = b[n:]
if num == trackFieldUse_fieldNumber && typ == protowire.VarintType {
v, _ := protowire.ConsumeVarint(b)
tracked = protowire.DecodeBool(v)
}
m := protowire.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
}