blob: 8d13d34164dfc60e481b4e5b58f42109a275fca4 [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
Joe Tsai5d72cc22019-03-28 01:13:26 -070013 "github.com/golang/protobuf/v2/proto"
Joe Tsaib6405bd2018-11-15 14:44:37 -080014 "github.com/golang/protobuf/v2/protogen"
15 "github.com/golang/protobuf/v2/reflect/protoreflect"
Joe Tsai5d72cc22019-03-28 01:13:26 -070016
17 descriptorpb "github.com/golang/protobuf/v2/types/descriptor"
Joe Tsaib6405bd2018-11-15 14:44:37 -080018)
19
Joe Tsaib6405bd2018-11-15 14:44:37 -080020// TODO: Add support for proto options.
21
Damien Neil8012b442019-01-18 09:32:24 -080022func genReflectFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
Damien Neil8012b442019-01-18 09:32:24 -080023 g.P("var ", f.GoDescriptorIdent, " ", protoreflectPackage.Ident("FileDescriptor"))
24 g.P()
25
Joe Tsai5d72cc22019-03-28 01:13:26 -070026 genFileDescriptor(gen, g, f)
Damien Neil8012b442019-01-18 09:32:24 -080027 if len(f.allEnums) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -080028 g.P("var ", enumTypesVarName(f), " = make([]", protoreflectPackage.Ident("EnumType"), ",", len(f.allEnums), ")")
Joe Tsaib6405bd2018-11-15 14:44:37 -080029 }
Damien Neil8012b442019-01-18 09:32:24 -080030 if len(f.allMessages) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -080031 g.P("var ", messageTypesVarName(f), " = make([]", protoimplPackage.Ident("MessageType"), ",", len(f.allMessages), ")")
Damien Neil8012b442019-01-18 09:32:24 -080032 }
33
34 // Generate a unique list of Go types for all declarations and dependencies,
35 // and the associated index into the type list for all dependencies.
36 var goTypes []string
37 var depIdxs []string
38 seen := map[protoreflect.FullName]int{}
39 genDep := func(name protoreflect.FullName, depSource string) {
40 if depSource != "" {
41 line := fmt.Sprintf("%d, // %s -> %s", seen[name], depSource, name)
42 depIdxs = append(depIdxs, line)
43 }
44 }
45 genEnum := func(e *protogen.Enum, depSource string) {
46 if e != nil {
47 name := e.Desc.FullName()
48 if _, ok := seen[name]; !ok {
49 line := fmt.Sprintf("(%s)(0), // %d: %s", g.QualifiedGoIdent(e.GoIdent), len(goTypes), name)
50 goTypes = append(goTypes, line)
51 seen[name] = len(seen)
52 }
53 if depSource != "" {
54 genDep(name, depSource)
55 }
56 }
57 }
58 genMessage := func(m *protogen.Message, depSource string) {
59 if m != nil {
60 name := m.Desc.FullName()
61 if _, ok := seen[name]; !ok {
62 line := fmt.Sprintf("(*%s)(nil), // %d: %s", g.QualifiedGoIdent(m.GoIdent), len(goTypes), name)
63 if m.Desc.IsMapEntry() {
64 // Map entry messages have no associated Go type.
65 line = fmt.Sprintf("nil, // %d: %s", len(goTypes), name)
66 }
67 goTypes = append(goTypes, line)
68 seen[name] = len(seen)
69 }
70 if depSource != "" {
71 genDep(name, depSource)
72 }
73 }
74 }
75
76 // This ordering is significant. See protoimpl.FileBuilder.GoTypes.
77 for _, enum := range f.allEnums {
78 genEnum(enum, "")
79 }
80 for _, message := range f.allMessages {
81 genMessage(message, "")
82 }
83 for _, extension := range f.allExtensions {
84 source := string(extension.Desc.FullName())
Joe Tsaid24bc722019-04-15 23:39:09 -070085 genMessage(extension.Extendee, source+":extendee")
Damien Neil8012b442019-01-18 09:32:24 -080086 }
87 for _, message := range f.allMessages {
88 for _, field := range message.Fields {
89 if field.Desc.IsWeak() {
90 continue
91 }
92 source := string(field.Desc.FullName())
Joe Tsaid24bc722019-04-15 23:39:09 -070093 genEnum(field.Enum, source+":type_name")
94 genMessage(field.Message, source+":type_name")
Damien Neil8012b442019-01-18 09:32:24 -080095 }
96 }
97 for _, extension := range f.allExtensions {
98 source := string(extension.Desc.FullName())
Joe Tsaid24bc722019-04-15 23:39:09 -070099 genEnum(extension.Enum, source+":type_name")
100 genMessage(extension.Message, source+":type_name")
Damien Neil8012b442019-01-18 09:32:24 -0800101 }
102 for _, service := range f.Services {
103 for _, method := range service.Methods {
104 source := string(method.Desc.FullName())
Joe Tsaid24bc722019-04-15 23:39:09 -0700105 genMessage(method.Input, source+":input_type")
106 genMessage(method.Output, source+":output_type")
Damien Neil8012b442019-01-18 09:32:24 -0800107 }
108 }
109 if len(depIdxs) > math.MaxInt32 {
110 panic("too many dependencies") // sanity check
111 }
112
113 g.P("var ", goTypesVarName(f), " = []interface{}{")
114 for _, s := range goTypes {
115 g.P(s)
116 }
117 g.P("}")
118
119 g.P("var ", depIdxsVarName(f), " = []int32{")
120 for _, s := range depIdxs {
121 g.P(s)
122 }
123 g.P("}")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800124
Damien Neil0fc22452019-03-08 17:18:11 -0800125 g.P("func init() { ", initFuncName(f.File), "() }")
126
127 g.P("func ", initFuncName(f.File), "() {")
128 g.P("if ", f.GoDescriptorIdent, " != nil {")
129 g.P("return")
130 g.P("}")
131
132 // Ensure that initialization functions for different files in the same Go
133 // package run in the correct order: Call the init funcs for every .proto file
134 // imported by this one that is in the same Go package.
135 for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
136 impFile, _ := gen.FileByName(imps.Get(i).Path())
137 if impFile.GoImportPath != f.GoImportPath {
138 continue
139 }
140 g.P(initFuncName(impFile), "()")
141 }
142
Damien Neil8012b442019-01-18 09:32:24 -0800143 if len(f.allExtensions) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800144 g.P("extensionTypes := make([]", protoreflectPackage.Ident("ExtensionType"), ",", len(f.allExtensions), ")")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800145 }
146
Damien Neil8012b442019-01-18 09:32:24 -0800147 g.P(f.GoDescriptorIdent, " = ", protoimplPackage.Ident("FileBuilder"), "{")
Joe Tsai5d72cc22019-03-28 01:13:26 -0700148 g.P("RawDescriptor: ", rawDescVarName(f), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800149 g.P("GoTypes: ", goTypesVarName(f), ",")
150 g.P("DependencyIndexes: ", depIdxsVarName(f), ",")
Joe Tsaiafb455e2019-03-14 16:08:22 -0700151 if len(f.allExtensions) > 0 {
152 g.P("LegacyExtensions: ", extDecsVarName(f), ",")
153 }
Damien Neil8012b442019-01-18 09:32:24 -0800154 if len(f.allEnums) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800155 g.P("EnumOutputTypes: ", enumTypesVarName(f), ",")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800156 }
Damien Neil8012b442019-01-18 09:32:24 -0800157 if len(f.allMessages) > 0 {
Joe Tsai35ec98f2019-03-25 14:41:32 -0700158 g.P("MessageOutputTypes: ", messageTypesVarName(f), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800159 }
160 if len(f.allExtensions) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800161 g.P("ExtensionOutputTypes: extensionTypes,")
Damien Neil8012b442019-01-18 09:32:24 -0800162 }
Joe Tsai35ec98f2019-03-25 14:41:32 -0700163 g.P("FilesRegistry: ", protoregistryPackage.Ident("GlobalFiles"), ",")
164 g.P("TypesRegistry: ", protoregistryPackage.Ident("GlobalTypes"), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800165 g.P("}.Init()")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800166
Joe Tsai5d72cc22019-03-28 01:13:26 -0700167 // Set inputs to nil to allow GC to reclaim resources.
168 g.P(rawDescVarName(f), " = nil")
169 g.P(goTypesVarName(f), " = nil")
170 g.P(depIdxsVarName(f), " = nil")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800171 g.P("}")
172}
173
Joe Tsai5d72cc22019-03-28 01:13:26 -0700174func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
175 // TODO: Replace this with v2 Clone.
176 descProto := new(descriptorpb.FileDescriptorProto)
177 b, err := proto.Marshal(f.Proto)
178 if err != nil {
179 gen.Error(err)
180 return
181 }
182 if err := proto.Unmarshal(b, descProto); err != nil {
183 gen.Error(err)
184 return
185 }
186
187 // Trim the source_code_info from the descriptor.
188 descProto.SourceCodeInfo = nil
189 b, err = proto.MarshalOptions{Deterministic: true}.Marshal(descProto)
190 if err != nil {
191 gen.Error(err)
192 return
193 }
194
195 g.P("var ", rawDescVarName(f), " = []byte{")
196 for len(b) > 0 {
197 n := 16
198 if n > len(b) {
199 n = len(b)
200 }
201
202 s := ""
203 for _, c := range b[:n] {
204 s += fmt.Sprintf("0x%02x,", c)
205 }
206 g.P(s)
207
208 b = b[n:]
209 }
210 g.P("}")
211 g.P()
212
Joe Tsaiab61d412019-04-16 15:23:29 -0700213 if generateRawDescMethods {
214 onceVar := rawDescVarName(f) + "Once"
215 dataVar := rawDescVarName(f) + "Data"
216 g.P("var (")
217 g.P(onceVar, " ", syncPackage.Ident("Once"))
218 g.P(dataVar, " = ", rawDescVarName(f))
219 g.P(")")
220 g.P()
Joe Tsai5d72cc22019-03-28 01:13:26 -0700221
Joe Tsaiab61d412019-04-16 15:23:29 -0700222 g.P("func ", rawDescVarName(f), "GZIP() []byte {")
223 g.P(onceVar, ".Do(func() {")
224 g.P(dataVar, " = ", protoimplPackage.Ident("X"), ".CompressGZIP(", dataVar, ")")
225 g.P("})")
226 g.P("return ", dataVar)
227 g.P("}")
228 g.P()
229 }
Joe Tsai5d72cc22019-03-28 01:13:26 -0700230}
231
Joe Tsaib6405bd2018-11-15 14:44:37 -0800232func genReflectEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, enum *protogen.Enum) {
Joe Tsai9667c482018-12-05 15:42:52 -0800233 idx := f.allEnumsByPtr[enum]
Joe Tsaib6405bd2018-11-15 14:44:37 -0800234 typesVar := enumTypesVarName(f)
Joe Tsai61968ce2019-04-01 12:59:24 -0700235
Joe Tsai0fc49f82019-05-01 12:29:25 -0700236 // Descriptor method.
237 g.P("func (", enum.GoIdent, ") Descriptor() ", protoreflectPackage.Ident("EnumDescriptor"), " {")
238 g.P("return ", typesVar, "[", idx, "].Descriptor()")
239 g.P("}")
240 g.P()
241
Joe Tsai61968ce2019-04-01 12:59:24 -0700242 // Type method.
Joe Tsai0fc49f82019-05-01 12:29:25 -0700243 // TODO: Remove this.
244 g.P("// Deprecated: Use Descriptor instead.")
Joe Tsai61968ce2019-04-01 12:59:24 -0700245 g.P("func (", enum.GoIdent, ") Type() ", protoreflectPackage.Ident("EnumType"), " {")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800246 g.P("return ", typesVar, "[", idx, "]")
247 g.P("}")
Joe Tsai61968ce2019-04-01 12:59:24 -0700248 g.P()
249
250 // Number method.
251 g.P("func (x ", enum.GoIdent, ") Number() ", protoreflectPackage.Ident("EnumNumber"), " {")
252 g.P("return ", protoreflectPackage.Ident("EnumNumber"), "(x)")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800253 g.P("}")
Joe Tsai61968ce2019-04-01 12:59:24 -0700254 g.P()
Joe Tsaib6405bd2018-11-15 14:44:37 -0800255}
256
257func genReflectMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message) {
Joe Tsai9667c482018-12-05 15:42:52 -0800258 idx := f.allMessagesByPtr[message]
Joe Tsaib6405bd2018-11-15 14:44:37 -0800259 typesVar := messageTypesVarName(f)
Joe Tsai61968ce2019-04-01 12:59:24 -0700260
261 // ProtoReflect method.
262 g.P("func (x *", message.GoIdent, ") ProtoReflect() ", protoreflectPackage.Ident("Message"), " {")
263 g.P("return ", typesVar, "[", idx, "].MessageOf(x)")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800264 g.P("}")
Joe Tsai61968ce2019-04-01 12:59:24 -0700265 g.P()
Damien Neil0d3e8cc2019-04-01 13:31:55 -0700266 g.P("func (m *", message.GoIdent, ") XXX_Methods() *", protoifacePackage.Ident("Methods"), " {")
267 g.P("return ", typesVar, "[", idx, "].Methods()")
268 g.P("}")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800269}
270
Joe Tsai7ca70982019-04-15 13:57:56 -0700271func fileVarName(f *protogen.File, suffix string) string {
272 prefix := f.GoDescriptorIdent.GoName
273 _, n := utf8.DecodeRuneInString(prefix)
274 prefix = strings.ToLower(prefix[:n]) + prefix[n:]
275 return prefix + "_" + suffix
276}
Joe Tsai5d72cc22019-03-28 01:13:26 -0700277func rawDescVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700278 return fileVarName(f.File, "rawDesc")
Joe Tsai5d72cc22019-03-28 01:13:26 -0700279}
Damien Neil8012b442019-01-18 09:32:24 -0800280func goTypesVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700281 return fileVarName(f.File, "goTypes")
Damien Neil8012b442019-01-18 09:32:24 -0800282}
283func depIdxsVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700284 return fileVarName(f.File, "depIdxs")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800285}
286func enumTypesVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700287 return fileVarName(f.File, "enumTypes")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800288}
289func messageTypesVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700290 return fileVarName(f.File, "msgTypes")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800291}
Joe Tsaiafb455e2019-03-14 16:08:22 -0700292func extDecsVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700293 return fileVarName(f.File, "extDescs")
Joe Tsaiafb455e2019-03-14 16:08:22 -0700294}
Damien Neil0fc22452019-03-08 17:18:11 -0800295func initFuncName(f *protogen.File) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700296 return fileVarName(f, "init")
Damien Neil0fc22452019-03-08 17:18:11 -0800297}