mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-01-01 11:58:21 +00:00
6bb8dec7f6
Using arrays in the generated reflection information adds unnecessary eq and hash functions being added to the package. Change to slices to reduce bloat. Change-Id: I1a4f6d59021644d93dd6c24679b9233141e89a75 Reviewed-on: https://go-review.googlesource.com/c/164640 Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
248 lines
8.0 KiB
Go
248 lines
8.0 KiB
Go
// Copyright 2018 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 (
|
|
"fmt"
|
|
"math"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/golang/protobuf/v2/protogen"
|
|
"github.com/golang/protobuf/v2/reflect/protoreflect"
|
|
)
|
|
|
|
// TODO: Remove this flag.
|
|
// Remember to remove the copy in internal/protogen/goldentest.
|
|
var enableReflectFlag = os.Getenv("PROTOC_GEN_GO_ENABLE_REFLECT") != ""
|
|
|
|
func enableReflection(f *protogen.File) bool {
|
|
return enableReflectFlag || isDescriptor(f)
|
|
}
|
|
|
|
// TODO: Remove special-casing for descriptor proto.
|
|
func isDescriptor(f *protogen.File) bool {
|
|
return f.Desc.Path() == "google/protobuf/descriptor.proto" && f.Desc.Package() == "google.protobuf"
|
|
}
|
|
|
|
// minimumVersion is minimum version of the v2 proto package that is required.
|
|
// This is incremented every time the generated code relies on some property
|
|
// in the proto package that was introduced in a later version.
|
|
const minimumVersion = 0
|
|
|
|
const (
|
|
reflectPackage = protogen.GoImportPath("reflect")
|
|
protoimplPackage = protogen.GoImportPath("github.com/golang/protobuf/v2/runtime/protoimpl")
|
|
protoreflectPackage = protogen.GoImportPath("github.com/golang/protobuf/v2/reflect/protoreflect")
|
|
prototypePackage = protogen.GoImportPath("github.com/golang/protobuf/v2/reflect/prototype")
|
|
)
|
|
|
|
// TODO: Add support for proto options.
|
|
|
|
func genReflectFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
|
|
if !enableReflection(f.File) {
|
|
return
|
|
}
|
|
|
|
// Emit a static check that enforces a minimum version of the proto package.
|
|
// TODO: This should appear higher up in the Go source file.
|
|
g.P("const _ = ", protoimplPackage.Ident("EnforceVersion"), "(", protoimplPackage.Ident("Version"), " - ", minimumVersion, ")")
|
|
|
|
g.P("var ", f.GoDescriptorIdent, " ", protoreflectPackage.Ident("FileDescriptor"))
|
|
g.P()
|
|
|
|
if len(f.allEnums) > 0 {
|
|
g.P("var ", enumTypesVarName(f), " = make([]", protoreflectPackage.Ident("EnumType"), ",", len(f.allEnums), ")")
|
|
}
|
|
if len(f.allMessages) > 0 {
|
|
g.P("var ", messageTypesVarName(f), " = make([]", protoimplPackage.Ident("MessageType"), ",", len(f.allMessages), ")")
|
|
}
|
|
|
|
// Generate a unique list of Go types for all declarations and dependencies,
|
|
// and the associated index into the type list for all dependencies.
|
|
var goTypes []string
|
|
var depIdxs []string
|
|
seen := map[protoreflect.FullName]int{}
|
|
genDep := func(name protoreflect.FullName, depSource string) {
|
|
if depSource != "" {
|
|
line := fmt.Sprintf("%d, // %s -> %s", seen[name], depSource, name)
|
|
depIdxs = append(depIdxs, line)
|
|
}
|
|
}
|
|
genEnum := func(e *protogen.Enum, depSource string) {
|
|
if e != nil {
|
|
name := e.Desc.FullName()
|
|
if _, ok := seen[name]; !ok {
|
|
line := fmt.Sprintf("(%s)(0), // %d: %s", g.QualifiedGoIdent(e.GoIdent), len(goTypes), name)
|
|
goTypes = append(goTypes, line)
|
|
seen[name] = len(seen)
|
|
}
|
|
if depSource != "" {
|
|
genDep(name, depSource)
|
|
}
|
|
}
|
|
}
|
|
genMessage := func(m *protogen.Message, depSource string) {
|
|
if m != nil {
|
|
name := m.Desc.FullName()
|
|
if _, ok := seen[name]; !ok {
|
|
line := fmt.Sprintf("(*%s)(nil), // %d: %s", g.QualifiedGoIdent(m.GoIdent), len(goTypes), name)
|
|
if m.Desc.IsMapEntry() {
|
|
// Map entry messages have no associated Go type.
|
|
line = fmt.Sprintf("nil, // %d: %s", len(goTypes), name)
|
|
}
|
|
goTypes = append(goTypes, line)
|
|
seen[name] = len(seen)
|
|
}
|
|
if depSource != "" {
|
|
genDep(name, depSource)
|
|
}
|
|
}
|
|
}
|
|
|
|
// This ordering is significant. See protoimpl.FileBuilder.GoTypes.
|
|
for _, enum := range f.allEnums {
|
|
genEnum(enum, "")
|
|
}
|
|
for _, message := range f.allMessages {
|
|
genMessage(message, "")
|
|
}
|
|
for _, extension := range f.allExtensions {
|
|
source := string(extension.Desc.FullName())
|
|
genMessage(extension.ExtendedType, source+":extendee")
|
|
}
|
|
for _, message := range f.allMessages {
|
|
for _, field := range message.Fields {
|
|
if field.Desc.IsWeak() {
|
|
continue
|
|
}
|
|
source := string(field.Desc.FullName())
|
|
genEnum(field.EnumType, source+":type_name")
|
|
genMessage(field.MessageType, source+":type_name")
|
|
}
|
|
}
|
|
for _, extension := range f.allExtensions {
|
|
source := string(extension.Desc.FullName())
|
|
genEnum(extension.EnumType, source+":type_name")
|
|
genMessage(extension.MessageType, source+":type_name")
|
|
}
|
|
for _, service := range f.Services {
|
|
for _, method := range service.Methods {
|
|
source := string(method.Desc.FullName())
|
|
genMessage(method.InputType, source+":input_type")
|
|
genMessage(method.OutputType, source+":output_type")
|
|
}
|
|
}
|
|
if len(depIdxs) > math.MaxInt32 {
|
|
panic("too many dependencies") // sanity check
|
|
}
|
|
|
|
g.P("var ", goTypesVarName(f), " = []interface{}{")
|
|
for _, s := range goTypes {
|
|
g.P(s)
|
|
}
|
|
g.P("}")
|
|
|
|
g.P("var ", depIdxsVarName(f), " = []int32{")
|
|
for _, s := range depIdxs {
|
|
g.P(s)
|
|
}
|
|
g.P("}")
|
|
|
|
g.P("func init() {")
|
|
if len(f.allMessages) > 0 {
|
|
g.P("messageTypes := make([]", protoreflectPackage.Ident("MessageType"), ",", len(f.allMessages), ")")
|
|
}
|
|
if len(f.allExtensions) > 0 {
|
|
g.P("extensionTypes := make([]", protoreflectPackage.Ident("ExtensionType"), ",", len(f.allExtensions), ")")
|
|
}
|
|
|
|
g.P(f.GoDescriptorIdent, " = ", protoimplPackage.Ident("FileBuilder"), "{")
|
|
g.P("RawDescriptor: ", f.descriptorRawVar, ",")
|
|
g.P("GoTypes: ", goTypesVarName(f), ",")
|
|
g.P("DependencyIndexes: ", depIdxsVarName(f), ",")
|
|
if len(f.allEnums) > 0 {
|
|
g.P("EnumOutputTypes: ", enumTypesVarName(f), ",")
|
|
}
|
|
if len(f.allMessages) > 0 {
|
|
g.P("MessageOutputTypes: messageTypes,")
|
|
}
|
|
if len(f.allExtensions) > 0 {
|
|
g.P("ExtensionOutputTypes: extensionTypes,")
|
|
}
|
|
g.P("}.Init()")
|
|
|
|
// Copy the local list of message types into the global array.
|
|
if len(f.allMessages) > 0 {
|
|
g.P("messageGoTypes := ", goTypesVarName(f), "[", len(f.allEnums), ":][:", len(f.allMessages), "]")
|
|
g.P("for i, mt := range messageTypes {")
|
|
g.P(messageTypesVarName(f), "[i].GoType = ", reflectPackage.Ident("TypeOf"), "(messageGoTypes[i])")
|
|
g.P(messageTypesVarName(f), "[i].PBType = mt")
|
|
g.P("}")
|
|
}
|
|
|
|
// Copy the local list of extension types into each global variable.
|
|
for i, extension := range f.allExtensions {
|
|
g.P(extensionVar(f.File, extension), ".Type = extensionTypes[", i, "]")
|
|
}
|
|
|
|
// TODO: Add v2 registration and stop v1 registration in genInitFunction.
|
|
|
|
// The descriptor proto needs to register the option types with the
|
|
// prototype so that the package can properly handle those option types.
|
|
if isDescriptor(f.File) {
|
|
for _, m := range f.allMessages {
|
|
name := m.GoIdent.GoName
|
|
if strings.HasSuffix(name, "Options") {
|
|
g.P(prototypePackage.Ident("X"), ".Register", name, "((*", name, ")(nil))")
|
|
}
|
|
}
|
|
}
|
|
|
|
g.P(goTypesVarName(f), " = nil") // allow GC to reclaim resource
|
|
g.P(depIdxsVarName(f), " = nil") // allow GC to reclaim resource
|
|
g.P("}")
|
|
}
|
|
|
|
func genReflectEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, enum *protogen.Enum) {
|
|
if !enableReflection(f.File) {
|
|
return
|
|
}
|
|
|
|
idx := f.allEnumsByPtr[enum]
|
|
typesVar := enumTypesVarName(f)
|
|
g.P("func (e ", enum.GoIdent, ") Type() ", protoreflectPackage.Ident("EnumType"), " {")
|
|
g.P("return ", typesVar, "[", idx, "]")
|
|
g.P("}")
|
|
g.P("func (e ", enum.GoIdent, ") Number() ", protoreflectPackage.Ident("EnumNumber"), " {")
|
|
g.P("return ", protoreflectPackage.Ident("EnumNumber"), "(e)")
|
|
g.P("}")
|
|
}
|
|
|
|
func genReflectMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message) {
|
|
if !enableReflection(f.File) {
|
|
return
|
|
}
|
|
|
|
idx := f.allMessagesByPtr[message]
|
|
typesVar := messageTypesVarName(f)
|
|
g.P("func (m *", message.GoIdent, ") ProtoReflect() ", protoreflectPackage.Ident("Message"), " {")
|
|
g.P("return ", typesVar, "[", idx, "].MessageOf(m)")
|
|
g.P("}")
|
|
}
|
|
|
|
func goTypesVarName(f *fileInfo) string {
|
|
return "xxx_" + f.GoDescriptorIdent.GoName + "_goTypes"
|
|
}
|
|
func depIdxsVarName(f *fileInfo) string {
|
|
return "xxx_" + f.GoDescriptorIdent.GoName + "_depIdxs"
|
|
}
|
|
func enumTypesVarName(f *fileInfo) string {
|
|
return "xxx_" + f.GoDescriptorIdent.GoName + "_enumTypes"
|
|
}
|
|
func messageTypesVarName(f *fileInfo) string {
|
|
return "xxx_" + f.GoDescriptorIdent.GoName + "_messageTypes"
|
|
}
|