blob: 862a4644b9055b571c402c2dcb7cb0d45716625e [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 Tsaid8881392019-06-06 13:01:53 -0700156 g.P("out := ", protoimplPackage.Ident("TypeBuilder"), "{")
157 g.P("File: ", protoimplPackage.Ident("DescBuilder"), "{")
Joe Tsai5d72cc22019-03-28 01:13:26 -0700158 g.P("RawDescriptor: ", rawDescVarName(f), ",")
Joe Tsaid8881392019-06-06 13:01:53 -0700159 g.P("NumEnums: ", len(f.allEnums), ",")
160 g.P("NumMessages: ", len(f.allMessages), ",")
161 g.P("NumExtensions: ", len(f.allExtensions), ",")
162 g.P("NumServices: ", len(f.Services), ",")
163 g.P("},")
Damien Neil8012b442019-01-18 09:32:24 -0800164 g.P("GoTypes: ", goTypesVarName(f), ",")
165 g.P("DependencyIndexes: ", depIdxsVarName(f), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800166 if len(f.allMessages) > 0 {
Joe Tsaid8881392019-06-06 13:01:53 -0700167 g.P("MessageInfos: ", messageTypesVarName(f), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800168 }
169 if len(f.allExtensions) > 0 {
Joe Tsaid8881392019-06-06 13:01:53 -0700170 g.P("LegacyExtensions: ", extDescsVarName(f), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800171 }
Joe Tsaid8881392019-06-06 13:01:53 -0700172 g.P("}.Build()")
173 g.P(f.GoDescriptorIdent, " = out.File")
174 if len(f.allEnums) > 0 {
175 g.P(enumTypesVarName(f), " = out.Enums")
176 }
Joe Tsaib6405bd2018-11-15 14:44:37 -0800177
Joe Tsai5d72cc22019-03-28 01:13:26 -0700178 // Set inputs to nil to allow GC to reclaim resources.
179 g.P(rawDescVarName(f), " = nil")
180 g.P(goTypesVarName(f), " = nil")
181 g.P(depIdxsVarName(f), " = nil")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800182 g.P("}")
183}
184
Joe Tsai5d72cc22019-03-28 01:13:26 -0700185func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
Joe Tsai5d72cc22019-03-28 01:13:26 -0700186 descProto := new(descriptorpb.FileDescriptorProto)
Joe Tsai8b0b71e2019-07-08 11:33:50 -0700187 proto.Merge(descProto, f.Proto)
188 descProto.SourceCodeInfo = nil // drop source code information
Joe Tsai5d72cc22019-03-28 01:13:26 -0700189
Joe Tsai8b0b71e2019-07-08 11:33:50 -0700190 b, err := proto.MarshalOptions{AllowPartial: true, Deterministic: true}.Marshal(descProto)
Joe Tsai5d72cc22019-03-28 01:13:26 -0700191 if err != nil {
192 gen.Error(err)
193 return
194 }
195
196 g.P("var ", rawDescVarName(f), " = []byte{")
197 for len(b) > 0 {
198 n := 16
199 if n > len(b) {
200 n = len(b)
201 }
202
203 s := ""
204 for _, c := range b[:n] {
205 s += fmt.Sprintf("0x%02x,", c)
206 }
207 g.P(s)
208
209 b = b[n:]
210 }
211 g.P("}")
212 g.P()
213
Joe Tsaiab61d412019-04-16 15:23:29 -0700214 if generateRawDescMethods {
215 onceVar := rawDescVarName(f) + "Once"
216 dataVar := rawDescVarName(f) + "Data"
217 g.P("var (")
218 g.P(onceVar, " ", syncPackage.Ident("Once"))
219 g.P(dataVar, " = ", rawDescVarName(f))
220 g.P(")")
221 g.P()
Joe Tsai5d72cc22019-03-28 01:13:26 -0700222
Joe Tsaiab61d412019-04-16 15:23:29 -0700223 g.P("func ", rawDescVarName(f), "GZIP() []byte {")
224 g.P(onceVar, ".Do(func() {")
225 g.P(dataVar, " = ", protoimplPackage.Ident("X"), ".CompressGZIP(", dataVar, ")")
226 g.P("})")
227 g.P("return ", dataVar)
228 g.P("}")
229 g.P()
230 }
Joe Tsai5d72cc22019-03-28 01:13:26 -0700231}
232
Joe Tsaib6405bd2018-11-15 14:44:37 -0800233func genReflectEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, enum *protogen.Enum) {
Joe Tsai9667c482018-12-05 15:42:52 -0800234 idx := f.allEnumsByPtr[enum]
Joe Tsaib6405bd2018-11-15 14:44:37 -0800235 typesVar := enumTypesVarName(f)
Joe Tsai61968ce2019-04-01 12:59:24 -0700236
Joe Tsai0fc49f82019-05-01 12:29:25 -0700237 // Descriptor method.
238 g.P("func (", enum.GoIdent, ") Descriptor() ", protoreflectPackage.Ident("EnumDescriptor"), " {")
Joe Tsaid8881392019-06-06 13:01:53 -0700239 g.P("return ", typesVar, "[", idx, "].EnumDescriptor")
Joe Tsai0fc49f82019-05-01 12:29:25 -0700240 g.P("}")
241 g.P()
242
Joe Tsai61968ce2019-04-01 12:59:24 -0700243 // Number method.
244 g.P("func (x ", enum.GoIdent, ") Number() ", protoreflectPackage.Ident("EnumNumber"), " {")
245 g.P("return ", protoreflectPackage.Ident("EnumNumber"), "(x)")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800246 g.P("}")
Joe Tsai61968ce2019-04-01 12:59:24 -0700247 g.P()
Joe Tsaib6405bd2018-11-15 14:44:37 -0800248}
249
250func genReflectMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message) {
Joe Tsai9667c482018-12-05 15:42:52 -0800251 idx := f.allMessagesByPtr[message]
Joe Tsaib6405bd2018-11-15 14:44:37 -0800252 typesVar := messageTypesVarName(f)
Joe Tsai61968ce2019-04-01 12:59:24 -0700253
254 // ProtoReflect method.
255 g.P("func (x *", message.GoIdent, ") ProtoReflect() ", protoreflectPackage.Ident("Message"), " {")
256 g.P("return ", typesVar, "[", idx, "].MessageOf(x)")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800257 g.P("}")
Joe Tsai61968ce2019-04-01 12:59:24 -0700258 g.P()
Damien Neil0d3e8cc2019-04-01 13:31:55 -0700259 g.P("func (m *", message.GoIdent, ") XXX_Methods() *", protoifacePackage.Ident("Methods"), " {")
260 g.P("return ", typesVar, "[", idx, "].Methods()")
261 g.P("}")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800262}
263
Joe Tsai7ca70982019-04-15 13:57:56 -0700264func fileVarName(f *protogen.File, suffix string) string {
265 prefix := f.GoDescriptorIdent.GoName
266 _, n := utf8.DecodeRuneInString(prefix)
267 prefix = strings.ToLower(prefix[:n]) + prefix[n:]
268 return prefix + "_" + suffix
269}
Joe Tsai5d72cc22019-03-28 01:13:26 -0700270func rawDescVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700271 return fileVarName(f.File, "rawDesc")
Joe Tsai5d72cc22019-03-28 01:13:26 -0700272}
Damien Neil8012b442019-01-18 09:32:24 -0800273func goTypesVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700274 return fileVarName(f.File, "goTypes")
Damien Neil8012b442019-01-18 09:32:24 -0800275}
276func depIdxsVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700277 return fileVarName(f.File, "depIdxs")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800278}
279func enumTypesVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700280 return fileVarName(f.File, "enumTypes")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800281}
282func messageTypesVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700283 return fileVarName(f.File, "msgTypes")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800284}
Joe Tsaid8881392019-06-06 13:01:53 -0700285func extDescsVarName(f *fileInfo) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700286 return fileVarName(f.File, "extDescs")
Joe Tsaiafb455e2019-03-14 16:08:22 -0700287}
Damien Neil0fc22452019-03-08 17:18:11 -0800288func initFuncName(f *protogen.File) string {
Joe Tsai7ca70982019-04-15 13:57:56 -0700289 return fileVarName(f, "init")
Damien Neil0fc22452019-03-08 17:18:11 -0800290}