Skip to content

Commit c0e4bb2

Browse files
committed
cmd/protoc-gen-go: unexport implementation-specific XXX fields
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 #276 Change-Id: Idf5c1f158973fa1c61187ff41440acb21c5dac94 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/185141 Reviewed-by: Damien Neil <[email protected]>
1 parent 0991227 commit c0e4bb2

File tree

82 files changed

+11864
-5789
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+11864
-5789
lines changed

cmd/protoc-gen-go-grpc/testdata/grpc/grpc.pb.go

Lines changed: 26 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/protoc-gen-go/internal_gengo/main.go

Lines changed: 83 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,22 @@ const (
3939
// generateOneofWrapperMethods specifies whether to generate
4040
// XXX_OneofWrappers methods on messages with oneofs.
4141
generateOneofWrapperMethods = false
42+
43+
// generateNoUnkeyedLiteralFields specifies whether to generate
44+
// the XXX_NoUnkeyedLiteral field.
45+
generateNoUnkeyedLiteralFields = false
46+
47+
// generateExportedSizeCacheFields specifies whether to generate an exported
48+
// XXX_sizecache field instead of an unexported sizeCache field.
49+
generateExportedSizeCacheFields = false
50+
51+
// generateExportedUnknownFields specifies whether to generate an exported
52+
// XXX_unrecognized field instead of an unexported unknownFields field.
53+
generateExportedUnknownFields = false
54+
55+
// generateExportedExtensionFields specifies whether to generate an exported
56+
// XXX_InternalExtensions field instead of an unexported extensionFields field.
57+
generateExportedExtensionFields = false
4258
)
4359

4460
const (
@@ -54,11 +70,28 @@ const (
5470
type fileInfo struct {
5571
*protogen.File
5672

57-
allEnums []*protogen.Enum
58-
allEnumsByPtr map[*protogen.Enum]int // value is index into allEnums
59-
allMessages []*protogen.Message
60-
allMessagesByPtr map[*protogen.Message]int // value is index into allMessages
61-
allExtensions []*protogen.Extension
73+
allEnums []*protogen.Enum
74+
allMessages []*protogen.Message
75+
allExtensions []*protogen.Extension
76+
77+
allEnumsByPtr map[*protogen.Enum]int // value is index into allEnums
78+
allMessagesByPtr map[*protogen.Message]int // value is index into allMessages
79+
allMessageFieldsByPtr map[*protogen.Message]*structFields
80+
}
81+
82+
type structFields struct {
83+
count int
84+
unexported map[int]string
85+
}
86+
87+
func (sf *structFields) append(name string) {
88+
if r, _ := utf8.DecodeRuneInString(name); !unicode.IsUpper(r) {
89+
if sf.unexported == nil {
90+
sf.unexported = make(map[int]string)
91+
}
92+
sf.unexported[sf.count] = name
93+
}
94+
sf.count++
6295
}
6396

6497
// GenerateFile generates the contents of a .pb.go file.
@@ -90,8 +123,10 @@ func GenerateFile(gen *protogen.Plugin, file *protogen.File) *protogen.Generated
90123
}
91124
if len(f.allMessages) > 0 {
92125
f.allMessagesByPtr = make(map[*protogen.Message]int)
126+
f.allMessageFieldsByPtr = make(map[*protogen.Message]*structFields)
93127
for i, m := range f.allMessages {
94128
f.allMessagesByPtr[m] = i
129+
f.allMessageFieldsByPtr[m] = new(structFields)
95130
}
96131
}
97132

@@ -347,15 +382,28 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, me
347382
}
348383
g.Annotate(message.GoIdent.GoName, message.Location)
349384
g.P("type ", message.GoIdent, " struct {")
385+
sf := f.allMessageFieldsByPtr[message]
350386
for _, field := range message.Fields {
351387
if field.Oneof != nil {
352388
// It would be a bit simpler to iterate over the oneofs below,
353389
// but generating the field here keeps the contents of the Go
354390
// struct in the same order as the contents of the source
355391
// .proto file.
356-
if field == field.Oneof.Fields[0] {
357-
genOneofField(gen, g, f, message, field.Oneof)
392+
oneof := field.Oneof
393+
if field != oneof.Fields[0] {
394+
continue // already generated oneof field for first entry
395+
}
396+
if g.PrintLeadingComments(oneof.Location) {
397+
g.P("//")
398+
}
399+
g.P("// Types that are valid to be assigned to ", oneofFieldName(oneof), ":")
400+
for _, field := range oneof.Fields {
401+
g.PrintLeadingComments(field.Location)
402+
g.P("//\t*", fieldOneofType(field))
358403
}
404+
g.Annotate(message.GoIdent.GoName+"."+oneofFieldName(oneof), oneof.Location)
405+
g.P(oneofFieldName(oneof), " ", oneofInterfaceName(oneof), " `protobuf_oneof:\"", oneof.Desc.Name(), "\"`")
406+
sf.append(oneofFieldName(oneof))
359407
continue
360408
}
361409
g.PrintLeadingComments(field.Location)
@@ -378,19 +426,42 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, me
378426
g.Annotate(message.GoIdent.GoName+"."+field.GoName, field.Location)
379427
g.P(field.GoName, " ", goType, " `", strings.Join(tags, " "), "`",
380428
deprecationComment(field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated()))
429+
sf.append(field.GoName)
381430
}
382-
g.P("XXX_NoUnkeyedLiteral struct{} `json:\"-\"`")
383431

432+
if generateNoUnkeyedLiteralFields {
433+
g.P("XXX_NoUnkeyedLiteral", " struct{} `json:\"-\"`")
434+
sf.append("XXX_NoUnkeyedLiteral")
435+
}
436+
if generateExportedSizeCacheFields {
437+
g.P("XXX_sizecache", " ", protoimplPackage.Ident("SizeCache"), " `json:\"-\"`")
438+
sf.append("XXX_sizecache")
439+
} else {
440+
g.P("sizeCache", " ", protoimplPackage.Ident("SizeCache"))
441+
sf.append("sizeCache")
442+
}
443+
if generateExportedUnknownFields {
444+
g.P("XXX_unrecognized", " ", protoimplPackage.Ident("UnknownFields"), " `json:\"-\"`")
445+
sf.append("XXX_unrecognized")
446+
} else {
447+
g.P("unknownFields", " ", protoimplPackage.Ident("UnknownFields"))
448+
sf.append("unknownFields")
449+
}
384450
if message.Desc.ExtensionRanges().Len() > 0 {
451+
// TODO: Remove this tag when we drop v1 support.
385452
var tags []string
386453
if message.Desc.Options().(*descriptorpb.MessageOptions).GetMessageSetWireFormat() {
387454
tags = append(tags, `protobuf_messageset:"1"`)
388455
}
389-
tags = append(tags, `json:"-"`)
390-
g.P("XXX_InternalExtensions ", protoimplPackage.Ident("ExtensionFields"), " `", strings.Join(tags, " "), "`")
456+
if generateExportedExtensionFields {
457+
tags = append(tags, `json:"-"`)
458+
g.P("XXX_InternalExtensions", " ", protoimplPackage.Ident("ExtensionFields"), " `", strings.Join(tags, " "), "`")
459+
sf.append("XXX_InternalExtensions")
460+
} else {
461+
g.P("extensionFields", " ", protoimplPackage.Ident("ExtensionFields"), " `", strings.Join(tags, " "), "`")
462+
sf.append("extensionFields")
463+
}
391464
}
392-
g.P("XXX_unrecognized ", protoimplPackage.Ident("UnknownFields"), " `json:\"-\"`")
393-
g.P("XXX_sizecache ", protoimplPackage.Ident("SizeCache"), " `json:\"-\"`")
394465
g.P("}")
395466
g.P()
396467

@@ -744,20 +815,6 @@ var wellKnownTypes = map[protoreflect.FullName]bool{
744815
"google.protobuf.Value": true,
745816
}
746817

747-
// genOneofField generates the struct field for a oneof.
748-
func genOneofField(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message, oneof *protogen.Oneof) {
749-
if g.PrintLeadingComments(oneof.Location) {
750-
g.P("//")
751-
}
752-
g.P("// Types that are valid to be assigned to ", oneofFieldName(oneof), ":")
753-
for _, field := range oneof.Fields {
754-
g.PrintLeadingComments(field.Location)
755-
g.P("//\t*", fieldOneofType(field))
756-
}
757-
g.Annotate(message.GoIdent.GoName+"."+oneofFieldName(oneof), oneof.Location)
758-
g.P(oneofFieldName(oneof), " ", oneofInterfaceName(oneof), " `protobuf_oneof:\"", oneof.Desc.Name(), "\"`")
759-
}
760-
761818
// genOneofGetter generate a Get method for a oneof.
762819
func genOneofGetter(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message, oneof *protogen.Oneof) {
763820
g.Annotate(message.GoIdent.GoName+".Get"+oneof.GoName, oneof.Location)

cmd/protoc-gen-go/internal_gengo/reflect.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,27 @@ func genReflectFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f
154154
}
155155

156156
if len(f.allMessages) > 0 {
157+
// Populate MessageInfo.Exporters.
158+
g.P("if !", protoimplPackage.Ident("UnsafeEnabled"), " {")
159+
for _, message := range f.allMessages {
160+
if sf := f.allMessageFieldsByPtr[message]; len(sf.unexported) > 0 {
161+
idx := f.allMessagesByPtr[message]
162+
typesVar := messageTypesVarName(f)
163+
164+
g.P(typesVar, "[", idx, "].Exporter = func(v interface{}, i int) interface{} {")
165+
g.P("switch v := v.(*", message.GoIdent, "); i {")
166+
for i := 0; i < sf.count; i++ {
167+
if name := sf.unexported[i]; name != "" {
168+
g.P("case ", i, ": return &v.", name)
169+
}
170+
}
171+
g.P("default: return nil")
172+
g.P("}")
173+
g.P("}")
174+
}
175+
}
176+
g.P("}")
177+
157178
// Populate MessageInfo.OneofWrappers.
158179
for _, message := range f.allMessages {
159180
if len(message.Oneofs) > 0 {

cmd/protoc-gen-go/testdata/annotations/annotations.pb.go

Lines changed: 15 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
annotation:{path:5 path:0 source_file:"annotations/annotations.proto" begin:631 end:650} annotation:{path:5 path:0 path:2 path:0 source_file:"annotations/annotations.proto" begin:667 end:714} annotation:{path:4 path:0 source_file:"annotations/annotations.proto" begin:1821 end:1843} annotation:{path:4 path:0 path:2 path:0 source_file:"annotations/annotations.proto" begin:1854 end:1874} annotation:{path:4 path:0 path:2 path:0 source_file:"annotations/annotations.proto" begin:2895 end:2918}
1+
annotation:{path:5 path:0 source_file:"annotations/annotations.proto" begin:631 end:650} annotation:{path:5 path:0 path:2 path:0 source_file:"annotations/annotations.proto" begin:667 end:714} annotation:{path:4 path:0 source_file:"annotations/annotations.proto" begin:1821 end:1843} annotation:{path:4 path:0 path:2 path:0 source_file:"annotations/annotations.proto" begin:1854 end:1874} annotation:{path:4 path:0 path:2 path:0 source_file:"annotations/annotations.proto" begin:2796 end:2819}

0 commit comments

Comments
 (0)