mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-01-29 09:32:38 +00:00
c0e4bb2054
We modify protoc-gen-go to stop generating exported XXX fields. The unsafe implementation is unaffected by this change since unsafe can access fields regardless of visibility. However, for the purego implementation, we need to respect Go visibility rules as enforced by the reflect package. We work around this by generating a exporter function that given a reference to the message and the field to export, returns a reference to the unexported field value. This exporter function is protected by a constant such that it is not linked into the final binary in non-purego build environment. Updates golang/protobuf#276 Change-Id: Idf5c1f158973fa1c61187ff41440acb21c5dac94 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/185141 Reviewed-by: Damien Neil <dneil@google.com>
331 lines
9.9 KiB
Go
331 lines
9.9 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"
|
|
"strings"
|
|
"unicode/utf8"
|
|
|
|
"google.golang.org/protobuf/compiler/protogen"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/reflect/protoreflect"
|
|
|
|
"google.golang.org/protobuf/types/descriptorpb"
|
|
)
|
|
|
|
func genReflectFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
|
|
g.P("var ", f.GoDescriptorIdent, " ", protoreflectPackage.Ident("FileDescriptor"))
|
|
g.P()
|
|
|
|
genFileDescriptor(gen, g, f)
|
|
if len(f.allEnums) > 0 {
|
|
g.P("var ", enumTypesVarName(f), " = make([]", prototypePackage.Ident("Enum"), ",", len(f.allEnums), ")")
|
|
}
|
|
if len(f.allMessages) > 0 {
|
|
g.P("var ", messageTypesVarName(f), " = make([]", protoimplPackage.Ident("MessageInfo"), ",", 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 filetype.TypeBuilder.DependencyIndexes.
|
|
var depOffsets []string
|
|
for _, enum := range f.allEnums {
|
|
genEnum(enum, "")
|
|
}
|
|
for _, message := range f.allMessages {
|
|
genMessage(message, "")
|
|
}
|
|
depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of field type_name sub-list", len(depIdxs)))
|
|
for _, message := range f.allMessages {
|
|
for _, field := range message.Fields {
|
|
if field.Desc.IsWeak() {
|
|
continue
|
|
}
|
|
source := string(field.Desc.FullName())
|
|
genEnum(field.Enum, source+":type_name")
|
|
genMessage(field.Message, source+":type_name")
|
|
}
|
|
}
|
|
depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of extension extendee sub-list", len(depIdxs)))
|
|
for _, extension := range f.allExtensions {
|
|
source := string(extension.Desc.FullName())
|
|
genMessage(extension.Extendee, source+":extendee")
|
|
}
|
|
depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of extension type_name sub-list", len(depIdxs)))
|
|
for _, extension := range f.allExtensions {
|
|
source := string(extension.Desc.FullName())
|
|
genEnum(extension.Enum, source+":type_name")
|
|
genMessage(extension.Message, source+":type_name")
|
|
}
|
|
depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of method input_type sub-list", len(depIdxs)))
|
|
for _, service := range f.Services {
|
|
for _, method := range service.Methods {
|
|
source := string(method.Desc.FullName())
|
|
genMessage(method.Input, source+":input_type")
|
|
}
|
|
}
|
|
depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of method output_type sub-list", len(depIdxs)))
|
|
for _, service := range f.Services {
|
|
for _, method := range service.Methods {
|
|
source := string(method.Desc.FullName())
|
|
genMessage(method.Output, source+":output_type")
|
|
}
|
|
}
|
|
for i := len(depOffsets) - 1; i >= 0; i-- {
|
|
depIdxs = append(depIdxs, depOffsets[i])
|
|
}
|
|
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() { ", initFuncName(f.File), "() }")
|
|
|
|
g.P("func ", initFuncName(f.File), "() {")
|
|
g.P("if ", f.GoDescriptorIdent, " != nil {")
|
|
g.P("return")
|
|
g.P("}")
|
|
|
|
// Ensure that initialization functions for different files in the same Go
|
|
// package run in the correct order: Call the init funcs for every .proto file
|
|
// imported by this one that is in the same Go package.
|
|
for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
|
|
impFile, _ := gen.FileByName(imps.Get(i).Path())
|
|
if impFile.GoImportPath != f.GoImportPath {
|
|
continue
|
|
}
|
|
g.P(initFuncName(impFile), "()")
|
|
}
|
|
|
|
if len(f.allMessages) > 0 {
|
|
// Populate MessageInfo.Exporters.
|
|
g.P("if !", protoimplPackage.Ident("UnsafeEnabled"), " {")
|
|
for _, message := range f.allMessages {
|
|
if sf := f.allMessageFieldsByPtr[message]; len(sf.unexported) > 0 {
|
|
idx := f.allMessagesByPtr[message]
|
|
typesVar := messageTypesVarName(f)
|
|
|
|
g.P(typesVar, "[", idx, "].Exporter = func(v interface{}, i int) interface{} {")
|
|
g.P("switch v := v.(*", message.GoIdent, "); i {")
|
|
for i := 0; i < sf.count; i++ {
|
|
if name := sf.unexported[i]; name != "" {
|
|
g.P("case ", i, ": return &v.", name)
|
|
}
|
|
}
|
|
g.P("default: return nil")
|
|
g.P("}")
|
|
g.P("}")
|
|
}
|
|
}
|
|
g.P("}")
|
|
|
|
// Populate MessageInfo.OneofWrappers.
|
|
for _, message := range f.allMessages {
|
|
if len(message.Oneofs) > 0 {
|
|
idx := f.allMessagesByPtr[message]
|
|
typesVar := messageTypesVarName(f)
|
|
|
|
// Associate the wrapper types by directly passing them to the MessageInfo.
|
|
g.P(typesVar, "[", idx, "].OneofWrappers = []interface{} {")
|
|
for _, oneof := range message.Oneofs {
|
|
for _, field := range oneof.Fields {
|
|
g.P("(*", fieldOneofType(field), ")(nil),")
|
|
}
|
|
}
|
|
g.P("}")
|
|
}
|
|
}
|
|
}
|
|
|
|
g.P("out := ", protoimplPackage.Ident("TypeBuilder"), "{")
|
|
g.P("File: ", protoimplPackage.Ident("DescBuilder"), "{")
|
|
g.P("RawDescriptor: ", rawDescVarName(f), ",")
|
|
g.P("NumEnums: ", len(f.allEnums), ",")
|
|
g.P("NumMessages: ", len(f.allMessages), ",")
|
|
g.P("NumExtensions: ", len(f.allExtensions), ",")
|
|
g.P("NumServices: ", len(f.Services), ",")
|
|
g.P("},")
|
|
g.P("GoTypes: ", goTypesVarName(f), ",")
|
|
g.P("DependencyIndexes: ", depIdxsVarName(f), ",")
|
|
if len(f.allMessages) > 0 {
|
|
g.P("MessageInfos: ", messageTypesVarName(f), ",")
|
|
}
|
|
if len(f.allExtensions) > 0 {
|
|
g.P("LegacyExtensions: ", extDescsVarName(f), ",")
|
|
}
|
|
g.P("}.Build()")
|
|
g.P(f.GoDescriptorIdent, " = out.File")
|
|
if len(f.allEnums) > 0 {
|
|
g.P(enumTypesVarName(f), " = out.Enums")
|
|
}
|
|
|
|
// Set inputs to nil to allow GC to reclaim resources.
|
|
g.P(rawDescVarName(f), " = nil")
|
|
g.P(goTypesVarName(f), " = nil")
|
|
g.P(depIdxsVarName(f), " = nil")
|
|
g.P("}")
|
|
}
|
|
|
|
func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
|
|
descProto := new(descriptorpb.FileDescriptorProto)
|
|
proto.Merge(descProto, f.Proto)
|
|
descProto.SourceCodeInfo = nil // drop source code information
|
|
|
|
b, err := proto.MarshalOptions{AllowPartial: true, Deterministic: true}.Marshal(descProto)
|
|
if err != nil {
|
|
gen.Error(err)
|
|
return
|
|
}
|
|
|
|
g.P("var ", rawDescVarName(f), " = []byte{")
|
|
for len(b) > 0 {
|
|
n := 16
|
|
if n > len(b) {
|
|
n = len(b)
|
|
}
|
|
|
|
s := ""
|
|
for _, c := range b[:n] {
|
|
s += fmt.Sprintf("0x%02x,", c)
|
|
}
|
|
g.P(s)
|
|
|
|
b = b[n:]
|
|
}
|
|
g.P("}")
|
|
g.P()
|
|
|
|
if generateRawDescMethods {
|
|
onceVar := rawDescVarName(f) + "Once"
|
|
dataVar := rawDescVarName(f) + "Data"
|
|
g.P("var (")
|
|
g.P(onceVar, " ", syncPackage.Ident("Once"))
|
|
g.P(dataVar, " = ", rawDescVarName(f))
|
|
g.P(")")
|
|
g.P()
|
|
|
|
g.P("func ", rawDescVarName(f), "GZIP() []byte {")
|
|
g.P(onceVar, ".Do(func() {")
|
|
g.P(dataVar, " = ", protoimplPackage.Ident("X"), ".CompressGZIP(", dataVar, ")")
|
|
g.P("})")
|
|
g.P("return ", dataVar)
|
|
g.P("}")
|
|
g.P()
|
|
}
|
|
}
|
|
|
|
func genReflectEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, enum *protogen.Enum) {
|
|
idx := f.allEnumsByPtr[enum]
|
|
typesVar := enumTypesVarName(f)
|
|
|
|
// Descriptor method.
|
|
g.P("func (", enum.GoIdent, ") Descriptor() ", protoreflectPackage.Ident("EnumDescriptor"), " {")
|
|
g.P("return ", typesVar, "[", idx, "].EnumDescriptor")
|
|
g.P("}")
|
|
g.P()
|
|
|
|
// Number method.
|
|
g.P("func (x ", enum.GoIdent, ") Number() ", protoreflectPackage.Ident("EnumNumber"), " {")
|
|
g.P("return ", protoreflectPackage.Ident("EnumNumber"), "(x)")
|
|
g.P("}")
|
|
g.P()
|
|
}
|
|
|
|
func genReflectMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message) {
|
|
idx := f.allMessagesByPtr[message]
|
|
typesVar := messageTypesVarName(f)
|
|
|
|
// ProtoReflect method.
|
|
g.P("func (x *", message.GoIdent, ") ProtoReflect() ", protoreflectPackage.Ident("Message"), " {")
|
|
g.P("return ", typesVar, "[", idx, "].MessageOf(x)")
|
|
g.P("}")
|
|
g.P()
|
|
g.P("func (m *", message.GoIdent, ") XXX_Methods() *", protoifacePackage.Ident("Methods"), " {")
|
|
g.P("return ", typesVar, "[", idx, "].Methods()")
|
|
g.P("}")
|
|
}
|
|
|
|
func fileVarName(f *protogen.File, suffix string) string {
|
|
prefix := f.GoDescriptorIdent.GoName
|
|
_, n := utf8.DecodeRuneInString(prefix)
|
|
prefix = strings.ToLower(prefix[:n]) + prefix[n:]
|
|
return prefix + "_" + suffix
|
|
}
|
|
func rawDescVarName(f *fileInfo) string {
|
|
return fileVarName(f.File, "rawDesc")
|
|
}
|
|
func goTypesVarName(f *fileInfo) string {
|
|
return fileVarName(f.File, "goTypes")
|
|
}
|
|
func depIdxsVarName(f *fileInfo) string {
|
|
return fileVarName(f.File, "depIdxs")
|
|
}
|
|
func enumTypesVarName(f *fileInfo) string {
|
|
return fileVarName(f.File, "enumTypes")
|
|
}
|
|
func messageTypesVarName(f *fileInfo) string {
|
|
return fileVarName(f.File, "msgTypes")
|
|
}
|
|
func extDescsVarName(f *fileInfo) string {
|
|
return fileVarName(f.File, "extDescs")
|
|
}
|
|
func initFuncName(f *protogen.File) string {
|
|
return fileVarName(f, "init")
|
|
}
|