blob: c58a3ef271b962d4598736ab66dff2ab0443647e [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
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 {
Joe Tsaid8881392019-06-06 13:01:53 -070028 g.P("var ", enumTypesVarName(f), " = make([]", prototypePackage.Ident("Enum"), ",", len(f.allEnums), ")")
Joe Tsaib6405bd2018-11-15 14:44:37 -080029 }
Damien Neil8012b442019-01-18 09:32:24 -080030 if len(f.allMessages) > 0 {
Joe Tsai4fe96632019-05-22 05:12:36 -040031 g.P("var ", messageTypesVarName(f), " = make([]", protoimplPackage.Ident("MessageInfo"), ",", 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
Joe Tsaid8881392019-06-06 13:01:53 -070076 // This ordering is significant.
77 // See filetype.TypeBuilder.DependencyIndexes.
78 var depOffsets []string
Damien Neil8012b442019-01-18 09:32:24 -080079 for _, enum := range f.allEnums {
80 genEnum(enum, "")
81 }
82 for _, message := range f.allMessages {
83 genMessage(message, "")
84 }
Joe Tsaid8881392019-06-06 13:01:53 -070085 depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of field type_name sub-list", len(depIdxs)))
Damien Neil8012b442019-01-18 09:32:24 -080086 for _, message := range f.allMessages {
87 for _, field := range message.Fields {
88 if field.Desc.IsWeak() {
89 continue
90 }
91 source := string(field.Desc.FullName())
Joe Tsaid24bc722019-04-15 23:39:09 -070092 genEnum(field.Enum, source+":type_name")
93 genMessage(field.Message, source+":type_name")
Damien Neil8012b442019-01-18 09:32:24 -080094 }
95 }
Joe Tsaid8881392019-06-06 13:01:53 -070096 depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of extension extendee sub-list", len(depIdxs)))
97 for _, extension := range f.allExtensions {
98 source := string(extension.Desc.FullName())
99 genMessage(extension.Extendee, source+":extendee")
100 }
101 depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of extension type_name sub-list", len(depIdxs)))
Damien Neil8012b442019-01-18 09:32:24 -0800102 for _, extension := range f.allExtensions {
103 source := string(extension.Desc.FullName())
Joe Tsaid24bc722019-04-15 23:39:09 -0700104 genEnum(extension.Enum, source+":type_name")
105 genMessage(extension.Message, source+":type_name")
Damien Neil8012b442019-01-18 09:32:24 -0800106 }
Joe Tsaid8881392019-06-06 13:01:53 -0700107 depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of method input_type sub-list", len(depIdxs)))
Damien Neil8012b442019-01-18 09:32:24 -0800108 for _, service := range f.Services {
109 for _, method := range service.Methods {
110 source := string(method.Desc.FullName())
Joe Tsaid24bc722019-04-15 23:39:09 -0700111 genMessage(method.Input, source+":input_type")
Joe Tsaid8881392019-06-06 13:01:53 -0700112 }
113 }
114 depOffsets = append(depOffsets, fmt.Sprintf("%d, // starting offset of method output_type sub-list", len(depIdxs)))
115 for _, service := range f.Services {
116 for _, method := range service.Methods {
117 source := string(method.Desc.FullName())
Joe Tsaid24bc722019-04-15 23:39:09 -0700118 genMessage(method.Output, source+":output_type")
Damien Neil8012b442019-01-18 09:32:24 -0800119 }
120 }
Joe Tsaid8881392019-06-06 13:01:53 -0700121 for i := len(depOffsets) - 1; i >= 0; i-- {
122 depIdxs = append(depIdxs, depOffsets[i])
123 }
Damien Neil8012b442019-01-18 09:32:24 -0800124 if len(depIdxs) > math.MaxInt32 {
125 panic("too many dependencies") // sanity check
126 }
127
128 g.P("var ", goTypesVarName(f), " = []interface{}{")
129 for _, s := range goTypes {
130 g.P(s)
131 }
132 g.P("}")
133
134 g.P("var ", depIdxsVarName(f), " = []int32{")
135 for _, s := range depIdxs {
136 g.P(s)
137 }
138 g.P("}")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800139
Damien Neil0fc22452019-03-08 17:18:11 -0800140 g.P("func init() { ", initFuncName(f.File), "() }")
141
142 g.P("func ", initFuncName(f.File), "() {")
143 g.P("if ", f.GoDescriptorIdent, " != nil {")
144 g.P("return")
145 g.P("}")
146
147 // Ensure that initialization functions for different files in the same Go
148 // package run in the correct order: Call the init funcs for every .proto file
149 // imported by this one that is in the same Go package.
150 for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
151 impFile, _ := gen.FileByName(imps.Get(i).Path())
152 if impFile.GoImportPath != f.GoImportPath {
153 continue
154 }
155 g.P(initFuncName(impFile), "()")
156 }
157
Joe Tsaid8881392019-06-06 13:01:53 -0700158 g.P("out := ", protoimplPackage.Ident("TypeBuilder"), "{")
159 g.P("File: ", protoimplPackage.Ident("DescBuilder"), "{")
Joe Tsai5d72cc22019-03-28 01:13:26 -0700160 g.P("RawDescriptor: ", rawDescVarName(f), ",")
Joe Tsaid8881392019-06-06 13:01:53 -0700161 g.P("NumEnums: ", len(f.allEnums), ",")
162 g.P("NumMessages: ", len(f.allMessages), ",")
163 g.P("NumExtensions: ", len(f.allExtensions), ",")
164 g.P("NumServices: ", len(f.Services), ",")
165 g.P("},")
Damien Neil8012b442019-01-18 09:32:24 -0800166 g.P("GoTypes: ", goTypesVarName(f), ",")
167 g.P("DependencyIndexes: ", depIdxsVarName(f), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800168 if len(f.allMessages) > 0 {
Joe Tsaid8881392019-06-06 13:01:53 -0700169 g.P("MessageInfos: ", messageTypesVarName(f), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800170 }
171 if len(f.allExtensions) > 0 {
Joe Tsaid8881392019-06-06 13:01:53 -0700172 g.P("LegacyExtensions: ", extDescsVarName(f), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800173 }
Joe Tsaid8881392019-06-06 13:01:53 -0700174 g.P("}.Build()")
175 g.P(f.GoDescriptorIdent, " = out.File")
176 if len(f.allEnums) > 0 {
177 g.P(enumTypesVarName(f), " = out.Enums")
178 }
Joe Tsaib6405bd2018-11-15 14:44:37 -0800179
Joe Tsai5d72cc22019-03-28 01:13:26 -0700180 // Set inputs to nil to allow GC to reclaim resources.
181 g.P(rawDescVarName(f), " = nil")
182 g.P(goTypesVarName(f), " = nil")
183 g.P(depIdxsVarName(f), " = nil")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800184 g.P("}")
185}
186
Joe Tsai5d72cc22019-03-28 01:13:26 -0700187func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
188 // TODO: Replace this with v2 Clone.
189 descProto := new(descriptorpb.FileDescriptorProto)
190 b, err := proto.Marshal(f.Proto)
191 if err != nil {
192 gen.Error(err)
193 return
194 }
195 if err := proto.Unmarshal(b, descProto); err != nil {
196 gen.Error(err)
197 return
198 }
199
200 // Trim the source_code_info from the descriptor.
201 descProto.SourceCodeInfo = nil
202 b, err = proto.MarshalOptions{Deterministic: true}.Marshal(descProto)
203 if err != nil {
204 gen.Error(err)
205 return
206 }
207
208 g.P("var ", rawDescVarName(f), " = []byte{")
209 for len(b) > 0 {
210 n := 16
211 if n > len(b) {
212 n = len(b)
213 }
214
215 s := ""
216 for _, c := range b[:n] {
217 s += fmt.Sprintf("0x%02x,", c)
218 }
219 g.P(s)
220
221 b = b[n:]
222 }
223 g.P("}")
224 g.P()
225
Joe Tsaiab61d412019-04-16 15:23:29 -0700226 if generateRawDescMethods {
227 onceVar := rawDescVarName(f) + "Once"
228 dataVar := rawDescVarName(f) + "Data"
229 g.P("var (")
230 g.P(onceVar, " ", syncPackage.Ident("Once"))
231 g.P(dataVar, " = ", rawDescVarName(f))
232 g.P(")")
233 g.P()
Joe Tsai5d72cc22019-03-28 01:13:26 -0700234
Joe Tsaiab61d412019-04-16 15:23:29 -0700235 g.P("func ", rawDescVarName(f), "GZIP() []byte {")
236 g.P(onceVar, ".Do(func() {")
237 g.P(dataVar, " = ", protoimplPackage.Ident("X"), ".CompressGZIP(", dataVar, ")")
238 g.P("})")
239 g.P("return ", dataVar)
240 g.P("}")
241 g.P()
242 }
Joe Tsai5d72cc22019-03-28 01:13:26 -0700243}
244
Joe Tsaib6405bd2018-11-15 14:44:37 -0800245func genReflectEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, enum *protogen.Enum) {
Joe Tsai9667c482018-12-05 15:42:52 -0800246 idx := f.allEnumsByPtr[enum]
Joe Tsaib6405bd2018-11-15 14:44:37 -0800247 typesVar := enumTypesVarName(f)
Joe Tsai61968ce2019-04-01 12:59:24 -0700248
Joe Tsai0fc49f82019-05-01 12:29:25 -0700249 // Descriptor method.
250 g.P("func (", enum.GoIdent, ") Descriptor() ", protoreflectPackage.Ident("EnumDescriptor"), " {")
Joe Tsaid8881392019-06-06 13:01:53 -0700251 g.P("return ", typesVar, "[", idx, "].EnumDescriptor")
Joe Tsai0fc49f82019-05-01 12:29:25 -0700252 g.P("}")
253 g.P()
254
Joe Tsai61968ce2019-04-01 12:59:24 -0700255 // Number method.
256 g.P("func (x ", enum.GoIdent, ") Number() ", protoreflectPackage.Ident("EnumNumber"), " {")
257 g.P("return ", protoreflectPackage.Ident("EnumNumber"), "(x)")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800258 g.P("}")
Joe Tsai61968ce2019-04-01 12:59:24 -0700259 g.P()
Joe Tsaib6405bd2018-11-15 14:44:37 -0800260}
261
262func genReflectMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message) {
Joe Tsai9667c482018-12-05 15:42:52 -0800263 idx := f.allMessagesByPtr[message]
Joe Tsaib6405bd2018-11-15 14:44:37 -0800264 typesVar := messageTypesVarName(f)
Joe Tsai61968ce2019-04-01 12:59:24 -0700265
266 // ProtoReflect method.
267 g.P("func (x *", message.GoIdent, ") ProtoReflect() ", protoreflectPackage.Ident("Message"), " {")
268 g.P("return ", typesVar, "[", idx, "].MessageOf(x)")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800269 g.P("}")
Joe Tsai61968ce2019-04-01 12:59:24 -0700270 g.P()
Damien Neil0d3e8cc2019-04-01 13:31:55 -0700271 g.P("func (m *", message.GoIdent, ") XXX_Methods() *", protoifacePackage.Ident("Methods"), " {")
272 g.P("return ", typesVar, "[", idx, "].Methods()")
273 g.P("}")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800274}
275
Joe Tsai7ca70982019-04-15 13:57:56 -0700276func fileVarName(f *protogen.File, suffix string) string {
277 prefix := f.GoDescriptorIdent.GoName
278 _, n := utf8.DecodeRuneInString(prefix)
279 prefix = strings.ToLower(prefix[:n]) + prefix[n:]
280 return prefix + "_" + suffix
281}
Joe Tsai5d72cc22019-03-28 01:13:26 -0700282func rawDescVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700283 return fileVarName(f.File, "rawDesc")
Joe Tsai5d72cc22019-03-28 01:13:26 -0700284}
Damien Neil8012b442019-01-18 09:32:24 -0800285func goTypesVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700286 return fileVarName(f.File, "goTypes")
Damien Neil8012b442019-01-18 09:32:24 -0800287}
288func depIdxsVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700289 return fileVarName(f.File, "depIdxs")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800290}
291func enumTypesVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700292 return fileVarName(f.File, "enumTypes")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800293}
294func messageTypesVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700295 return fileVarName(f.File, "msgTypes")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800296}
Joe Tsaid8881392019-06-06 13:01:53 -0700297func extDescsVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700298 return fileVarName(f.File, "extDescs")
Joe Tsaiafb455e2019-03-14 16:08:22 -0700299}
Damien Neil0fc22452019-03-08 17:18:11 -0800300func initFuncName(f *protogen.File) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700301 return fileVarName(f, "init")
Damien Neil0fc22452019-03-08 17:18:11 -0800302}