blob: e11dc15dd67e36ebfa5dbbeb81a0d725ce025a92 [file] [log] [blame]
Joe Tsaib6405bd2018-11-15 14:44:37 -08001// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package internal_gengo
6
7import (
8 "fmt"
9 "math"
Joe Tsaib6405bd2018-11-15 14:44:37 -080010 "strings"
11
12 "github.com/golang/protobuf/v2/protogen"
13 "github.com/golang/protobuf/v2/reflect/protoreflect"
14)
15
Joe Tsai24ceb2b2018-12-04 22:53:56 -080016// TODO: Remove special-casing for descriptor proto.
17func isDescriptor(f *protogen.File) bool {
18 return f.Desc.Path() == "google/protobuf/descriptor.proto" && f.Desc.Package() == "google.protobuf"
19}
Joe Tsaib6405bd2018-11-15 14:44:37 -080020
21// minimumVersion is minimum version of the v2 proto package that is required.
22// This is incremented every time the generated code relies on some property
23// in the proto package that was introduced in a later version.
24const minimumVersion = 0
25
26const (
Damien Neil8012b442019-01-18 09:32:24 -080027 reflectPackage = protogen.GoImportPath("reflect")
Joe Tsaib6405bd2018-11-15 14:44:37 -080028 protoimplPackage = protogen.GoImportPath("github.com/golang/protobuf/v2/runtime/protoimpl")
29 protoreflectPackage = protogen.GoImportPath("github.com/golang/protobuf/v2/reflect/protoreflect")
30 prototypePackage = protogen.GoImportPath("github.com/golang/protobuf/v2/reflect/prototype")
31)
32
33// TODO: Add support for proto options.
34
Damien Neil8012b442019-01-18 09:32:24 -080035func genReflectFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
Damien Neil8012b442019-01-18 09:32:24 -080036 // Emit a static check that enforces a minimum version of the proto package.
37 // TODO: This should appear higher up in the Go source file.
38 g.P("const _ = ", protoimplPackage.Ident("EnforceVersion"), "(", protoimplPackage.Ident("Version"), " - ", minimumVersion, ")")
39
40 g.P("var ", f.GoDescriptorIdent, " ", protoreflectPackage.Ident("FileDescriptor"))
41 g.P()
42
43 if len(f.allEnums) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -080044 g.P("var ", enumTypesVarName(f), " = make([]", protoreflectPackage.Ident("EnumType"), ",", len(f.allEnums), ")")
Joe Tsaib6405bd2018-11-15 14:44:37 -080045 }
Damien Neil8012b442019-01-18 09:32:24 -080046 if len(f.allMessages) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -080047 g.P("var ", messageTypesVarName(f), " = make([]", protoimplPackage.Ident("MessageType"), ",", len(f.allMessages), ")")
Damien Neil8012b442019-01-18 09:32:24 -080048 }
49
50 // Generate a unique list of Go types for all declarations and dependencies,
51 // and the associated index into the type list for all dependencies.
52 var goTypes []string
53 var depIdxs []string
54 seen := map[protoreflect.FullName]int{}
55 genDep := func(name protoreflect.FullName, depSource string) {
56 if depSource != "" {
57 line := fmt.Sprintf("%d, // %s -> %s", seen[name], depSource, name)
58 depIdxs = append(depIdxs, line)
59 }
60 }
61 genEnum := func(e *protogen.Enum, depSource string) {
62 if e != nil {
63 name := e.Desc.FullName()
64 if _, ok := seen[name]; !ok {
65 line := fmt.Sprintf("(%s)(0), // %d: %s", g.QualifiedGoIdent(e.GoIdent), len(goTypes), name)
66 goTypes = append(goTypes, line)
67 seen[name] = len(seen)
68 }
69 if depSource != "" {
70 genDep(name, depSource)
71 }
72 }
73 }
74 genMessage := func(m *protogen.Message, depSource string) {
75 if m != nil {
76 name := m.Desc.FullName()
77 if _, ok := seen[name]; !ok {
78 line := fmt.Sprintf("(*%s)(nil), // %d: %s", g.QualifiedGoIdent(m.GoIdent), len(goTypes), name)
79 if m.Desc.IsMapEntry() {
80 // Map entry messages have no associated Go type.
81 line = fmt.Sprintf("nil, // %d: %s", len(goTypes), name)
82 }
83 goTypes = append(goTypes, line)
84 seen[name] = len(seen)
85 }
86 if depSource != "" {
87 genDep(name, depSource)
88 }
89 }
90 }
91
92 // This ordering is significant. See protoimpl.FileBuilder.GoTypes.
93 for _, enum := range f.allEnums {
94 genEnum(enum, "")
95 }
96 for _, message := range f.allMessages {
97 genMessage(message, "")
98 }
99 for _, extension := range f.allExtensions {
100 source := string(extension.Desc.FullName())
101 genMessage(extension.ExtendedType, source+":extendee")
102 }
103 for _, message := range f.allMessages {
104 for _, field := range message.Fields {
105 if field.Desc.IsWeak() {
106 continue
107 }
108 source := string(field.Desc.FullName())
109 genEnum(field.EnumType, source+":type_name")
110 genMessage(field.MessageType, source+":type_name")
111 }
112 }
113 for _, extension := range f.allExtensions {
114 source := string(extension.Desc.FullName())
115 genEnum(extension.EnumType, source+":type_name")
116 genMessage(extension.MessageType, source+":type_name")
117 }
118 for _, service := range f.Services {
119 for _, method := range service.Methods {
120 source := string(method.Desc.FullName())
121 genMessage(method.InputType, source+":input_type")
122 genMessage(method.OutputType, source+":output_type")
123 }
124 }
125 if len(depIdxs) > math.MaxInt32 {
126 panic("too many dependencies") // sanity check
127 }
128
129 g.P("var ", goTypesVarName(f), " = []interface{}{")
130 for _, s := range goTypes {
131 g.P(s)
132 }
133 g.P("}")
134
135 g.P("var ", depIdxsVarName(f), " = []int32{")
136 for _, s := range depIdxs {
137 g.P(s)
138 }
139 g.P("}")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800140
Damien Neil0fc22452019-03-08 17:18:11 -0800141 g.P("func init() { ", initFuncName(f.File), "() }")
142
143 g.P("func ", initFuncName(f.File), "() {")
144 g.P("if ", f.GoDescriptorIdent, " != nil {")
145 g.P("return")
146 g.P("}")
147
148 // Ensure that initialization functions for different files in the same Go
149 // package run in the correct order: Call the init funcs for every .proto file
150 // imported by this one that is in the same Go package.
151 for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
152 impFile, _ := gen.FileByName(imps.Get(i).Path())
153 if impFile.GoImportPath != f.GoImportPath {
154 continue
155 }
156 g.P(initFuncName(impFile), "()")
157 }
158
Damien Neil8012b442019-01-18 09:32:24 -0800159 if len(f.allMessages) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800160 g.P("messageTypes := make([]", protoreflectPackage.Ident("MessageType"), ",", len(f.allMessages), ")")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800161 }
Damien Neil8012b442019-01-18 09:32:24 -0800162 if len(f.allExtensions) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800163 g.P("extensionTypes := make([]", protoreflectPackage.Ident("ExtensionType"), ",", len(f.allExtensions), ")")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800164 }
165
Damien Neil8012b442019-01-18 09:32:24 -0800166 g.P(f.GoDescriptorIdent, " = ", protoimplPackage.Ident("FileBuilder"), "{")
167 g.P("RawDescriptor: ", f.descriptorRawVar, ",")
168 g.P("GoTypes: ", goTypesVarName(f), ",")
169 g.P("DependencyIndexes: ", depIdxsVarName(f), ",")
170 if len(f.allEnums) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800171 g.P("EnumOutputTypes: ", enumTypesVarName(f), ",")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800172 }
Damien Neil8012b442019-01-18 09:32:24 -0800173 if len(f.allMessages) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800174 g.P("MessageOutputTypes: messageTypes,")
Damien Neil8012b442019-01-18 09:32:24 -0800175 }
176 if len(f.allExtensions) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800177 g.P("ExtensionOutputTypes: extensionTypes,")
Damien Neil8012b442019-01-18 09:32:24 -0800178 }
179 g.P("}.Init()")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800180
Damien Neil8012b442019-01-18 09:32:24 -0800181 // Copy the local list of message types into the global array.
182 if len(f.allMessages) > 0 {
183 g.P("messageGoTypes := ", goTypesVarName(f), "[", len(f.allEnums), ":][:", len(f.allMessages), "]")
Damien Neil6bb8dec2019-03-01 13:22:30 -0800184 g.P("for i, mt := range messageTypes {")
Damien Neil8012b442019-01-18 09:32:24 -0800185 g.P(messageTypesVarName(f), "[i].GoType = ", reflectPackage.Ident("TypeOf"), "(messageGoTypes[i])")
186 g.P(messageTypesVarName(f), "[i].PBType = mt")
187 g.P("}")
188 }
189
190 // Copy the local list of extension types into each global variable.
191 for i, extension := range f.allExtensions {
192 g.P(extensionVar(f.File, extension), ".Type = extensionTypes[", i, "]")
193 }
Joe Tsaib6405bd2018-11-15 14:44:37 -0800194
195 // TODO: Add v2 registration and stop v1 registration in genInitFunction.
196
Joe Tsaia4cbffe2018-12-06 13:01:52 -0800197 // The descriptor proto needs to register the option types with the
198 // prototype so that the package can properly handle those option types.
199 if isDescriptor(f.File) {
200 for _, m := range f.allMessages {
201 name := m.GoIdent.GoName
202 if strings.HasSuffix(name, "Options") {
203 g.P(prototypePackage.Ident("X"), ".Register", name, "((*", name, ")(nil))")
204 }
205 }
206 }
207
Damien Neil8012b442019-01-18 09:32:24 -0800208 g.P(goTypesVarName(f), " = nil") // allow GC to reclaim resource
209 g.P(depIdxsVarName(f), " = nil") // allow GC to reclaim resource
Joe Tsaib6405bd2018-11-15 14:44:37 -0800210 g.P("}")
211}
212
Joe Tsaib6405bd2018-11-15 14:44:37 -0800213func genReflectEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, enum *protogen.Enum) {
Joe Tsai9667c482018-12-05 15:42:52 -0800214 idx := f.allEnumsByPtr[enum]
Joe Tsaib6405bd2018-11-15 14:44:37 -0800215 typesVar := enumTypesVarName(f)
Damien Neila8593ba2019-01-08 16:18:07 -0800216 g.P("func (e ", enum.GoIdent, ") Type() ", protoreflectPackage.Ident("EnumType"), " {")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800217 g.P("return ", typesVar, "[", idx, "]")
218 g.P("}")
Damien Neila8593ba2019-01-08 16:18:07 -0800219 g.P("func (e ", enum.GoIdent, ") Number() ", protoreflectPackage.Ident("EnumNumber"), " {")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800220 g.P("return ", protoreflectPackage.Ident("EnumNumber"), "(e)")
221 g.P("}")
222}
223
224func genReflectMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message) {
Joe Tsai9667c482018-12-05 15:42:52 -0800225 idx := f.allMessagesByPtr[message]
Joe Tsaib6405bd2018-11-15 14:44:37 -0800226 typesVar := messageTypesVarName(f)
227 g.P("func (m *", message.GoIdent, ") ProtoReflect() ", protoreflectPackage.Ident("Message"), " {")
Damien Neil8012b442019-01-18 09:32:24 -0800228 g.P("return ", typesVar, "[", idx, "].MessageOf(m)")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800229 g.P("}")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800230}
231
Damien Neil8012b442019-01-18 09:32:24 -0800232func goTypesVarName(f *fileInfo) string {
233 return "xxx_" + f.GoDescriptorIdent.GoName + "_goTypes"
234}
235func depIdxsVarName(f *fileInfo) string {
236 return "xxx_" + f.GoDescriptorIdent.GoName + "_depIdxs"
Joe Tsaib6405bd2018-11-15 14:44:37 -0800237}
238func enumTypesVarName(f *fileInfo) string {
Damien Neil8012b442019-01-18 09:32:24 -0800239 return "xxx_" + f.GoDescriptorIdent.GoName + "_enumTypes"
Joe Tsaib6405bd2018-11-15 14:44:37 -0800240}
241func messageTypesVarName(f *fileInfo) string {
Damien Neil8012b442019-01-18 09:32:24 -0800242 return "xxx_" + f.GoDescriptorIdent.GoName + "_messageTypes"
Joe Tsaib6405bd2018-11-15 14:44:37 -0800243}
Damien Neil0fc22452019-03-08 17:18:11 -0800244func initFuncName(f *protogen.File) string {
245 return "xxx_" + f.GoDescriptorIdent.GoName + "_init"
246}