blob: 43099b2b90058921d73e459604b06dc9a4ce3b44 [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 Tsai7ca70982019-04-15 13:57:56 -070010 "strings"
11 "unicode/utf8"
Joe Tsaib6405bd2018-11-15 14:44:37 -080012
Damien Neil5c5b5312019-05-14 12:44:37 -070013 "google.golang.org/protobuf/compiler/protogen"
Damien Neile89e6242019-05-13 23:55:40 -070014 "google.golang.org/protobuf/proto"
Damien Neile89e6242019-05-13 23:55:40 -070015 "google.golang.org/protobuf/reflect/protoreflect"
Joe Tsai5d72cc22019-03-28 01:13:26 -070016
Joe Tsaia95b29f2019-05-16 12:47:20 -070017 "google.golang.org/protobuf/types/descriptorpb"
Joe Tsaib6405bd2018-11-15 14:44:37 -080018)
19
Damien Neil8012b442019-01-18 09:32:24 -080020func genReflectFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
Damien Neil8012b442019-01-18 09:32:24 -080021 g.P("var ", f.GoDescriptorIdent, " ", protoreflectPackage.Ident("FileDescriptor"))
22 g.P()
23
Joe Tsai5d72cc22019-03-28 01:13:26 -070024 genFileDescriptor(gen, g, f)
Damien Neil8012b442019-01-18 09:32:24 -080025 if len(f.allEnums) > 0 {
Joe Tsaid8881392019-06-06 13:01:53 -070026 g.P("var ", enumTypesVarName(f), " = make([]", prototypePackage.Ident("Enum"), ",", len(f.allEnums), ")")
Joe Tsaib6405bd2018-11-15 14:44:37 -080027 }
Damien Neil8012b442019-01-18 09:32:24 -080028 if len(f.allMessages) > 0 {
Joe Tsai4fe96632019-05-22 05:12:36 -040029 g.P("var ", messageTypesVarName(f), " = make([]", protoimplPackage.Ident("MessageInfo"), ",", len(f.allMessages), ")")
Damien Neil8012b442019-01-18 09:32:24 -080030 }
31
32 // Generate a unique list of Go types for all declarations and dependencies,
33 // and the associated index into the type list for all dependencies.
34 var goTypes []string
35 var depIdxs []string
36 seen := map[protoreflect.FullName]int{}
37 genDep := func(name protoreflect.FullName, depSource string) {
38 if depSource != "" {
39 line := fmt.Sprintf("%d, // %s -> %s", seen[name], depSource, name)
40 depIdxs = append(depIdxs, line)
41 }
42 }
43 genEnum := func(e *protogen.Enum, depSource string) {
44 if e != nil {
45 name := e.Desc.FullName()
46 if _, ok := seen[name]; !ok {
47 line := fmt.Sprintf("(%s)(0), // %d: %s", g.QualifiedGoIdent(e.GoIdent), len(goTypes), name)
48 goTypes = append(goTypes, line)
49 seen[name] = len(seen)
50 }
51 if depSource != "" {
52 genDep(name, depSource)
53 }
54 }
55 }
56 genMessage := func(m *protogen.Message, depSource string) {
57 if m != nil {
58 name := m.Desc.FullName()
59 if _, ok := seen[name]; !ok {
60 line := fmt.Sprintf("(*%s)(nil), // %d: %s", g.QualifiedGoIdent(m.GoIdent), len(goTypes), name)
61 if m.Desc.IsMapEntry() {
62 // Map entry messages have no associated Go type.
63 line = fmt.Sprintf("nil, // %d: %s", len(goTypes), name)
64 }
65 goTypes = append(goTypes, line)
66 seen[name] = len(seen)
67 }
68 if depSource != "" {
69 genDep(name, depSource)
70 }
71 }
72 }
73
Joe Tsaid8881392019-06-06 13:01:53 -070074 // This ordering is significant.
75 // See filetype.TypeBuilder.DependencyIndexes.
76 var depOffsets []string
Damien Neil8012b442019-01-18 09:32:24 -080077 for _, enum := range f.allEnums {
78 genEnum(enum, "")
79 }
80 for _, message := range f.allMessages {
81 genMessage(message, "")
82 }
Joe Tsaid8881392019-06-06 13:01:53 -070083 depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of field type_name sub-list", len(depIdxs)))
Damien Neil8012b442019-01-18 09:32:24 -080084 for _, message := range f.allMessages {
85 for _, field := range message.Fields {
86 if field.Desc.IsWeak() {
87 continue
88 }
89 source := string(field.Desc.FullName())
Joe Tsaid24bc722019-04-15 23:39:09 -070090 genEnum(field.Enum, source+":type_name")
91 genMessage(field.Message, source+":type_name")
Damien Neil8012b442019-01-18 09:32:24 -080092 }
93 }
Joe Tsaid8881392019-06-06 13:01:53 -070094 depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of extension extendee sub-list", len(depIdxs)))
95 for _, extension := range f.allExtensions {
96 source := string(extension.Desc.FullName())
97 genMessage(extension.Extendee, source+":extendee")
98 }
99 depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of extension type_name sub-list", len(depIdxs)))
Damien Neil8012b442019-01-18 09:32:24 -0800100 for _, extension := range f.allExtensions {
101 source := string(extension.Desc.FullName())
Joe Tsaid24bc722019-04-15 23:39:09 -0700102 genEnum(extension.Enum, source+":type_name")
103 genMessage(extension.Message, source+":type_name")
Damien Neil8012b442019-01-18 09:32:24 -0800104 }
Joe Tsaid8881392019-06-06 13:01:53 -0700105 depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of method input_type sub-list", len(depIdxs)))
Damien Neil8012b442019-01-18 09:32:24 -0800106 for _, service := range f.Services {
107 for _, method := range service.Methods {
108 source := string(method.Desc.FullName())
Joe Tsaid24bc722019-04-15 23:39:09 -0700109 genMessage(method.Input, source+":input_type")
Joe Tsaid8881392019-06-06 13:01:53 -0700110 }
111 }
112 depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of method output_type sub-list", len(depIdxs)))
113 for _, service := range f.Services {
114 for _, method := range service.Methods {
115 source := string(method.Desc.FullName())
Joe Tsaid24bc722019-04-15 23:39:09 -0700116 genMessage(method.Output, source+":output_type")
Damien Neil8012b442019-01-18 09:32:24 -0800117 }
118 }
Joe Tsaid8881392019-06-06 13:01:53 -0700119 for i := len(depOffsets) - 1; i >= 0; i-- {
120 depIdxs = append(depIdxs, depOffsets[i])
121 }
Damien Neil8012b442019-01-18 09:32:24 -0800122 if len(depIdxs) > math.MaxInt32 {
123 panic("too many dependencies") // sanity check
124 }
125
126 g.P("var ", goTypesVarName(f), " = []interface{}{")
127 for _, s := range goTypes {
128 g.P(s)
129 }
130 g.P("}")
131
132 g.P("var ", depIdxsVarName(f), " = []int32{")
133 for _, s := range depIdxs {
134 g.P(s)
135 }
136 g.P("}")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800137
Damien Neil0fc22452019-03-08 17:18:11 -0800138 g.P("func init() { ", initFuncName(f.File), "() }")
139
140 g.P("func ", initFuncName(f.File), "() {")
141 g.P("if ", f.GoDescriptorIdent, " != nil {")
142 g.P("return")
143 g.P("}")
144
145 // Ensure that initialization functions for different files in the same Go
146 // package run in the correct order: Call the init funcs for every .proto file
147 // imported by this one that is in the same Go package.
148 for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
149 impFile, _ := gen.FileByName(imps.Get(i).Path())
150 if impFile.GoImportPath != f.GoImportPath {
151 continue
152 }
153 g.P(initFuncName(impFile), "()")
154 }
155
Joe Tsai09912272019-07-08 10:38:11 -0700156 if len(f.allMessages) > 0 {
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700157 // 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
Joe Tsai09912272019-07-08 10:38:11 -0700178 // Populate MessageInfo.OneofWrappers.
179 for _, message := range f.allMessages {
180 if len(message.Oneofs) > 0 {
181 idx := f.allMessagesByPtr[message]
182 typesVar := messageTypesVarName(f)
183
184 // Associate the wrapper types by directly passing them to the MessageInfo.
185 g.P(typesVar, "[", idx, "].OneofWrappers = []interface{} {")
186 for _, oneof := range message.Oneofs {
187 for _, field := range oneof.Fields {
188 g.P("(*", fieldOneofType(field), ")(nil),")
189 }
190 }
191 g.P("}")
192 }
193 }
194 }
195
Joe Tsaid8881392019-06-06 13:01:53 -0700196 g.P("out := ", protoimplPackage.Ident("TypeBuilder"), "{")
197 g.P("File: ", protoimplPackage.Ident("DescBuilder"), "{")
Joe Tsai5d72cc22019-03-28 01:13:26 -0700198 g.P("RawDescriptor: ", rawDescVarName(f), ",")
Joe Tsaid8881392019-06-06 13:01:53 -0700199 g.P("NumEnums: ", len(f.allEnums), ",")
200 g.P("NumMessages: ", len(f.allMessages), ",")
201 g.P("NumExtensions: ", len(f.allExtensions), ",")
202 g.P("NumServices: ", len(f.Services), ",")
203 g.P("},")
Damien Neil8012b442019-01-18 09:32:24 -0800204 g.P("GoTypes: ", goTypesVarName(f), ",")
205 g.P("DependencyIndexes: ", depIdxsVarName(f), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800206 if len(f.allMessages) > 0 {
Joe Tsaid8881392019-06-06 13:01:53 -0700207 g.P("MessageInfos: ", messageTypesVarName(f), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800208 }
209 if len(f.allExtensions) > 0 {
Joe Tsaid8881392019-06-06 13:01:53 -0700210 g.P("LegacyExtensions: ", extDescsVarName(f), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800211 }
Joe Tsaid8881392019-06-06 13:01:53 -0700212 g.P("}.Build()")
213 g.P(f.GoDescriptorIdent, " = out.File")
214 if len(f.allEnums) > 0 {
215 g.P(enumTypesVarName(f), " = out.Enums")
216 }
Joe Tsaib6405bd2018-11-15 14:44:37 -0800217
Joe Tsai5d72cc22019-03-28 01:13:26 -0700218 // Set inputs to nil to allow GC to reclaim resources.
219 g.P(rawDescVarName(f), " = nil")
220 g.P(goTypesVarName(f), " = nil")
221 g.P(depIdxsVarName(f), " = nil")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800222 g.P("}")
223}
224
Joe Tsai5d72cc22019-03-28 01:13:26 -0700225func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
Joe Tsai5d72cc22019-03-28 01:13:26 -0700226 descProto := new(descriptorpb.FileDescriptorProto)
Joe Tsai8b0b71e2019-07-08 11:33:50 -0700227 proto.Merge(descProto, f.Proto)
228 descProto.SourceCodeInfo = nil // drop source code information
Joe Tsai5d72cc22019-03-28 01:13:26 -0700229
Joe Tsai8b0b71e2019-07-08 11:33:50 -0700230 b, err := proto.MarshalOptions{AllowPartial: true, Deterministic: true}.Marshal(descProto)
Joe Tsai5d72cc22019-03-28 01:13:26 -0700231 if err != nil {
232 gen.Error(err)
233 return
234 }
235
236 g.P("var ", rawDescVarName(f), " = []byte{")
237 for len(b) > 0 {
238 n := 16
239 if n > len(b) {
240 n = len(b)
241 }
242
243 s := ""
244 for _, c := range b[:n] {
245 s += fmt.Sprintf("0x%02x,", c)
246 }
247 g.P(s)
248
249 b = b[n:]
250 }
251 g.P("}")
252 g.P()
253
Joe Tsaiab61d412019-04-16 15:23:29 -0700254 if generateRawDescMethods {
255 onceVar := rawDescVarName(f) + "Once"
256 dataVar := rawDescVarName(f) + "Data"
257 g.P("var (")
258 g.P(onceVar, " ", syncPackage.Ident("Once"))
259 g.P(dataVar, " = ", rawDescVarName(f))
260 g.P(")")
261 g.P()
Joe Tsai5d72cc22019-03-28 01:13:26 -0700262
Joe Tsaiab61d412019-04-16 15:23:29 -0700263 g.P("func ", rawDescVarName(f), "GZIP() []byte {")
264 g.P(onceVar, ".Do(func() {")
265 g.P(dataVar, " = ", protoimplPackage.Ident("X"), ".CompressGZIP(", dataVar, ")")
266 g.P("})")
267 g.P("return ", dataVar)
268 g.P("}")
269 g.P()
270 }
Joe Tsai5d72cc22019-03-28 01:13:26 -0700271}
272
Joe Tsaib6405bd2018-11-15 14:44:37 -0800273func genReflectEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, enum *protogen.Enum) {
Joe Tsai9667c482018-12-05 15:42:52 -0800274 idx := f.allEnumsByPtr[enum]
Joe Tsaib6405bd2018-11-15 14:44:37 -0800275 typesVar := enumTypesVarName(f)
Joe Tsai61968ce2019-04-01 12:59:24 -0700276
Joe Tsai0fc49f82019-05-01 12:29:25 -0700277 // Descriptor method.
278 g.P("func (", enum.GoIdent, ") Descriptor() ", protoreflectPackage.Ident("EnumDescriptor"), " {")
Joe Tsaid8881392019-06-06 13:01:53 -0700279 g.P("return ", typesVar, "[", idx, "].EnumDescriptor")
Joe Tsai0fc49f82019-05-01 12:29:25 -0700280 g.P("}")
281 g.P()
282
Joe Tsai61968ce2019-04-01 12:59:24 -0700283 // Number method.
284 g.P("func (x ", enum.GoIdent, ") Number() ", protoreflectPackage.Ident("EnumNumber"), " {")
285 g.P("return ", protoreflectPackage.Ident("EnumNumber"), "(x)")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800286 g.P("}")
Joe Tsai61968ce2019-04-01 12:59:24 -0700287 g.P()
Joe Tsaib6405bd2018-11-15 14:44:37 -0800288}
289
290func genReflectMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message) {
Joe Tsai9667c482018-12-05 15:42:52 -0800291 idx := f.allMessagesByPtr[message]
Joe Tsaib6405bd2018-11-15 14:44:37 -0800292 typesVar := messageTypesVarName(f)
Joe Tsai61968ce2019-04-01 12:59:24 -0700293
294 // ProtoReflect method.
295 g.P("func (x *", message.GoIdent, ") ProtoReflect() ", protoreflectPackage.Ident("Message"), " {")
Joe Tsai82760ce2019-06-20 03:09:57 -0700296 g.P("mi := &", typesVar, "[", idx, "]")
297 if generateMessateStateFields {
298 g.P("if ", protoimplPackage.Ident("UnsafeEnabled"), " && x != nil {")
299 g.P("ms := ", protoimplPackage.Ident("X"), ".MessageStateOf(", protoimplPackage.Ident("Pointer"), "(x))")
300 g.P("if ms.LoadMessageInfo() == nil {")
301 g.P("ms.StoreMessageInfo(mi)")
302 g.P("}")
303 g.P("return ms")
304 g.P("}")
305 }
306 g.P("return mi.MessageOf(x)")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800307 g.P("}")
Joe Tsai61968ce2019-04-01 12:59:24 -0700308 g.P()
Joe Tsai82760ce2019-06-20 03:09:57 -0700309
310 // XXX_Methods method.
311 g.P("func (x *", message.GoIdent, ") XXX_Methods() *", protoifacePackage.Ident("Methods"), " {")
Damien Neil0d3e8cc2019-04-01 13:31:55 -0700312 g.P("return ", typesVar, "[", idx, "].Methods()")
313 g.P("}")
Joe Tsai82760ce2019-06-20 03:09:57 -0700314 g.P()
Joe Tsaib6405bd2018-11-15 14:44:37 -0800315}
316
Joe Tsai7ca70982019-04-15 13:57:56 -0700317func fileVarName(f *protogen.File, suffix string) string {
318 prefix := f.GoDescriptorIdent.GoName
319 _, n := utf8.DecodeRuneInString(prefix)
320 prefix = strings.ToLower(prefix[:n]) + prefix[n:]
321 return prefix + "_" + suffix
322}
Joe Tsai5d72cc22019-03-28 01:13:26 -0700323func rawDescVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700324 return fileVarName(f.File, "rawDesc")
Joe Tsai5d72cc22019-03-28 01:13:26 -0700325}
Damien Neil8012b442019-01-18 09:32:24 -0800326func goTypesVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700327 return fileVarName(f.File, "goTypes")
Damien Neil8012b442019-01-18 09:32:24 -0800328}
329func depIdxsVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700330 return fileVarName(f.File, "depIdxs")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800331}
332func enumTypesVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700333 return fileVarName(f.File, "enumTypes")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800334}
335func messageTypesVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700336 return fileVarName(f.File, "msgTypes")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800337}
Joe Tsaid8881392019-06-06 13:01:53 -0700338func extDescsVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700339 return fileVarName(f.File, "extDescs")
Joe Tsaiafb455e2019-03-14 16:08:22 -0700340}
Damien Neil0fc22452019-03-08 17:18:11 -0800341func initFuncName(f *protogen.File) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700342 return fileVarName(f, "init")
Damien Neil0fc22452019-03-08 17:18:11 -0800343}