blob: 4c684e444ac722521a8932e0ded9781149bd8fc9 [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
Joe Tsai5d72cc22019-03-28 01:13:26 -070011 "github.com/golang/protobuf/v2/proto"
Joe Tsaib6405bd2018-11-15 14:44:37 -080012 "github.com/golang/protobuf/v2/protogen"
13 "github.com/golang/protobuf/v2/reflect/protoreflect"
Joe Tsai5d72cc22019-03-28 01:13:26 -070014
15 descriptorpb "github.com/golang/protobuf/v2/types/descriptor"
Joe Tsaib6405bd2018-11-15 14:44:37 -080016)
17
Joe Tsaib6405bd2018-11-15 14:44:37 -080018// TODO: Add support for proto options.
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 {
Damien Neil6bb8dec2019-03-01 13:22:30 -080026 g.P("var ", enumTypesVarName(f), " = make([]", protoreflectPackage.Ident("EnumType"), ",", len(f.allEnums), ")")
Joe Tsaib6405bd2018-11-15 14:44:37 -080027 }
Damien Neil8012b442019-01-18 09:32:24 -080028 if len(f.allMessages) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -080029 g.P("var ", messageTypesVarName(f), " = make([]", protoimplPackage.Ident("MessageType"), ",", 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
74 // This ordering is significant. See protoimpl.FileBuilder.GoTypes.
75 for _, enum := range f.allEnums {
76 genEnum(enum, "")
77 }
78 for _, message := range f.allMessages {
79 genMessage(message, "")
80 }
81 for _, extension := range f.allExtensions {
82 source := string(extension.Desc.FullName())
83 genMessage(extension.ExtendedType, source+":extendee")
84 }
85 for _, message := range f.allMessages {
86 for _, field := range message.Fields {
87 if field.Desc.IsWeak() {
88 continue
89 }
90 source := string(field.Desc.FullName())
91 genEnum(field.EnumType, source+":type_name")
92 genMessage(field.MessageType, source+":type_name")
93 }
94 }
95 for _, extension := range f.allExtensions {
96 source := string(extension.Desc.FullName())
97 genEnum(extension.EnumType, source+":type_name")
98 genMessage(extension.MessageType, source+":type_name")
99 }
100 for _, service := range f.Services {
101 for _, method := range service.Methods {
102 source := string(method.Desc.FullName())
103 genMessage(method.InputType, source+":input_type")
104 genMessage(method.OutputType, source+":output_type")
105 }
106 }
107 if len(depIdxs) > math.MaxInt32 {
108 panic("too many dependencies") // sanity check
109 }
110
111 g.P("var ", goTypesVarName(f), " = []interface{}{")
112 for _, s := range goTypes {
113 g.P(s)
114 }
115 g.P("}")
116
117 g.P("var ", depIdxsVarName(f), " = []int32{")
118 for _, s := range depIdxs {
119 g.P(s)
120 }
121 g.P("}")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800122
Damien Neil0fc22452019-03-08 17:18:11 -0800123 g.P("func init() { ", initFuncName(f.File), "() }")
124
125 g.P("func ", initFuncName(f.File), "() {")
126 g.P("if ", f.GoDescriptorIdent, " != nil {")
127 g.P("return")
128 g.P("}")
129
130 // Ensure that initialization functions for different files in the same Go
131 // package run in the correct order: Call the init funcs for every .proto file
132 // imported by this one that is in the same Go package.
133 for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
134 impFile, _ := gen.FileByName(imps.Get(i).Path())
135 if impFile.GoImportPath != f.GoImportPath {
136 continue
137 }
138 g.P(initFuncName(impFile), "()")
139 }
140
Damien Neil8012b442019-01-18 09:32:24 -0800141 if len(f.allExtensions) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800142 g.P("extensionTypes := make([]", protoreflectPackage.Ident("ExtensionType"), ",", len(f.allExtensions), ")")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800143 }
144
Damien Neil8012b442019-01-18 09:32:24 -0800145 g.P(f.GoDescriptorIdent, " = ", protoimplPackage.Ident("FileBuilder"), "{")
Joe Tsai5d72cc22019-03-28 01:13:26 -0700146 g.P("RawDescriptor: ", rawDescVarName(f), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800147 g.P("GoTypes: ", goTypesVarName(f), ",")
148 g.P("DependencyIndexes: ", depIdxsVarName(f), ",")
Joe Tsaiafb455e2019-03-14 16:08:22 -0700149 if len(f.allExtensions) > 0 {
150 g.P("LegacyExtensions: ", extDecsVarName(f), ",")
151 }
Damien Neil8012b442019-01-18 09:32:24 -0800152 if len(f.allEnums) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800153 g.P("EnumOutputTypes: ", enumTypesVarName(f), ",")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800154 }
Damien Neil8012b442019-01-18 09:32:24 -0800155 if len(f.allMessages) > 0 {
Joe Tsai35ec98f2019-03-25 14:41:32 -0700156 g.P("MessageOutputTypes: ", messageTypesVarName(f), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800157 }
158 if len(f.allExtensions) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800159 g.P("ExtensionOutputTypes: extensionTypes,")
Damien Neil8012b442019-01-18 09:32:24 -0800160 }
Joe Tsai35ec98f2019-03-25 14:41:32 -0700161 g.P("FilesRegistry: ", protoregistryPackage.Ident("GlobalFiles"), ",")
162 g.P("TypesRegistry: ", protoregistryPackage.Ident("GlobalTypes"), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800163 g.P("}.Init()")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800164
Joe Tsai5d72cc22019-03-28 01:13:26 -0700165 // Set inputs to nil to allow GC to reclaim resources.
166 g.P(rawDescVarName(f), " = nil")
167 g.P(goTypesVarName(f), " = nil")
168 g.P(depIdxsVarName(f), " = nil")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800169 g.P("}")
170}
171
Joe Tsai5d72cc22019-03-28 01:13:26 -0700172func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
173 // TODO: Replace this with v2 Clone.
174 descProto := new(descriptorpb.FileDescriptorProto)
175 b, err := proto.Marshal(f.Proto)
176 if err != nil {
177 gen.Error(err)
178 return
179 }
180 if err := proto.Unmarshal(b, descProto); err != nil {
181 gen.Error(err)
182 return
183 }
184
185 // Trim the source_code_info from the descriptor.
186 descProto.SourceCodeInfo = nil
187 b, err = proto.MarshalOptions{Deterministic: true}.Marshal(descProto)
188 if err != nil {
189 gen.Error(err)
190 return
191 }
192
193 g.P("var ", rawDescVarName(f), " = []byte{")
194 for len(b) > 0 {
195 n := 16
196 if n > len(b) {
197 n = len(b)
198 }
199
200 s := ""
201 for _, c := range b[:n] {
202 s += fmt.Sprintf("0x%02x,", c)
203 }
204 g.P(s)
205
206 b = b[n:]
207 }
208 g.P("}")
209 g.P()
210
211 onceVar := rawDescVarName(f) + "_once"
212 dataVar := rawDescVarName(f) + "_data"
213 g.P("var (")
214 g.P(onceVar, " ", syncPackage.Ident("Once"))
215 g.P(dataVar, " = ", rawDescVarName(f))
216 g.P(")")
217 g.P()
218
219 g.P("func ", rawDescVarName(f), "GZIP() []byte {")
220 g.P(onceVar, ".Do(func() {")
221 g.P(dataVar, " = ", protoimplPackage.Ident("X"), ".CompressGZIP(", dataVar, ")")
222 g.P("})")
223 g.P("return ", dataVar)
224 g.P("}")
225 g.P()
226}
227
Joe Tsaib6405bd2018-11-15 14:44:37 -0800228func genReflectEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, enum *protogen.Enum) {
Joe Tsai9667c482018-12-05 15:42:52 -0800229 idx := f.allEnumsByPtr[enum]
Joe Tsaib6405bd2018-11-15 14:44:37 -0800230 typesVar := enumTypesVarName(f)
Joe Tsai61968ce2019-04-01 12:59:24 -0700231
232 // Type method.
233 g.P("func (", enum.GoIdent, ") Type() ", protoreflectPackage.Ident("EnumType"), " {")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800234 g.P("return ", typesVar, "[", idx, "]")
235 g.P("}")
Joe Tsai61968ce2019-04-01 12:59:24 -0700236 g.P()
237
238 // Number method.
239 g.P("func (x ", enum.GoIdent, ") Number() ", protoreflectPackage.Ident("EnumNumber"), " {")
240 g.P("return ", protoreflectPackage.Ident("EnumNumber"), "(x)")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800241 g.P("}")
Joe Tsai61968ce2019-04-01 12:59:24 -0700242 g.P()
Joe Tsaib6405bd2018-11-15 14:44:37 -0800243}
244
245func genReflectMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message) {
Joe Tsai9667c482018-12-05 15:42:52 -0800246 idx := f.allMessagesByPtr[message]
Joe Tsaib6405bd2018-11-15 14:44:37 -0800247 typesVar := messageTypesVarName(f)
Joe Tsai61968ce2019-04-01 12:59:24 -0700248
249 // ProtoReflect method.
250 g.P("func (x *", message.GoIdent, ") ProtoReflect() ", protoreflectPackage.Ident("Message"), " {")
251 g.P("return ", typesVar, "[", idx, "].MessageOf(x)")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800252 g.P("}")
Joe Tsai61968ce2019-04-01 12:59:24 -0700253 g.P()
Damien Neil0d3e8cc2019-04-01 13:31:55 -0700254 g.P("func (m *", message.GoIdent, ") XXX_Methods() *", protoifacePackage.Ident("Methods"), " {")
255 g.P("return ", typesVar, "[", idx, "].Methods()")
256 g.P("}")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800257}
258
Joe Tsai5d72cc22019-03-28 01:13:26 -0700259func rawDescVarName(f *fileInfo) string {
260 return "xxx_" + f.GoDescriptorIdent.GoName + "_rawDesc"
261}
Damien Neil8012b442019-01-18 09:32:24 -0800262func goTypesVarName(f *fileInfo) string {
263 return "xxx_" + f.GoDescriptorIdent.GoName + "_goTypes"
264}
265func depIdxsVarName(f *fileInfo) string {
266 return "xxx_" + f.GoDescriptorIdent.GoName + "_depIdxs"
Joe Tsaib6405bd2018-11-15 14:44:37 -0800267}
268func enumTypesVarName(f *fileInfo) string {
Damien Neil8012b442019-01-18 09:32:24 -0800269 return "xxx_" + f.GoDescriptorIdent.GoName + "_enumTypes"
Joe Tsaib6405bd2018-11-15 14:44:37 -0800270}
271func messageTypesVarName(f *fileInfo) string {
Damien Neil8012b442019-01-18 09:32:24 -0800272 return "xxx_" + f.GoDescriptorIdent.GoName + "_messageTypes"
Joe Tsaib6405bd2018-11-15 14:44:37 -0800273}
Joe Tsaiafb455e2019-03-14 16:08:22 -0700274func extDecsVarName(f *fileInfo) string {
275 return "xxx_" + f.GoDescriptorIdent.GoName + "_extDescs"
276}
Damien Neil0fc22452019-03-08 17:18:11 -0800277func initFuncName(f *protogen.File) string {
278 return "xxx_" + f.GoDescriptorIdent.GoName + "_init"
279}