1
2
3
4
5 package internal_gengo
6
7 import (
8 "fmt"
9 "math"
10 "strings"
11 "unicode/utf8"
12
13 "google.golang.org/protobuf/compiler/protogen"
14 "google.golang.org/protobuf/proto"
15 "google.golang.org/protobuf/reflect/protopath"
16 "google.golang.org/protobuf/reflect/protorange"
17 "google.golang.org/protobuf/reflect/protoreflect"
18
19 "google.golang.org/protobuf/types/descriptorpb"
20 )
21
22 func genReflectFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
23 g.P("var ", f.GoDescriptorIdent, " ", protoreflectPackage.Ident("FileDescriptor"))
24 g.P()
25
26 genFileDescriptor(gen, g, f)
27 if len(f.allEnums) > 0 {
28 g.P("var ", enumTypesVarName(f), " = make([]", protoimplPackage.Ident("EnumInfo"), ",", len(f.allEnums), ")")
29 }
30 if len(f.allMessages) > 0 {
31 g.P("var ", messageTypesVarName(f), " = make([]", protoimplPackage.Ident("MessageInfo"), ",", len(f.allMessages), ")")
32 }
33
34
35
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, // %d: %s -> %s", seen[name], len(depIdxs), 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
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
77
78 type offsetEntry struct {
79 start int
80 name string
81 }
82 var depOffsets []offsetEntry
83 for _, enum := range f.allEnums {
84 genEnum(enum.Enum, "")
85 }
86 for _, message := range f.allMessages {
87 genMessage(message.Message, "")
88 }
89 depOffsets = append(depOffsets, offsetEntry{len(depIdxs), "field type_name"})
90 for _, message := range f.allMessages {
91 for _, field := range message.Fields {
92 if field.Desc.IsWeak() {
93 continue
94 }
95 source := string(field.Desc.FullName())
96 genEnum(field.Enum, source+":type_name")
97 genMessage(field.Message, source+":type_name")
98 }
99 }
100 depOffsets = append(depOffsets, offsetEntry{len(depIdxs), "extension extendee"})
101 for _, extension := range f.allExtensions {
102 source := string(extension.Desc.FullName())
103 genMessage(extension.Extendee, source+":extendee")
104 }
105 depOffsets = append(depOffsets, offsetEntry{len(depIdxs), "extension type_name"})
106 for _, extension := range f.allExtensions {
107 source := string(extension.Desc.FullName())
108 genEnum(extension.Enum, source+":type_name")
109 genMessage(extension.Message, source+":type_name")
110 }
111 depOffsets = append(depOffsets, offsetEntry{len(depIdxs), "method input_type"})
112 for _, service := range f.Services {
113 for _, method := range service.Methods {
114 source := string(method.Desc.FullName())
115 genMessage(method.Input, source+":input_type")
116 }
117 }
118 depOffsets = append(depOffsets, offsetEntry{len(depIdxs), "method output_type"})
119 for _, service := range f.Services {
120 for _, method := range service.Methods {
121 source := string(method.Desc.FullName())
122 genMessage(method.Output, source+":output_type")
123 }
124 }
125 depOffsets = append(depOffsets, offsetEntry{len(depIdxs), ""})
126 for i := len(depOffsets) - 2; i >= 0; i-- {
127 curr, next := depOffsets[i], depOffsets[i+1]
128 depIdxs = append(depIdxs, fmt.Sprintf("%d, // [%d:%d] is the sub-list for %s",
129 curr.start, curr.start, next.start, curr.name))
130 }
131 if len(depIdxs) > math.MaxInt32 {
132 panic("too many dependencies")
133 }
134
135 g.P("var ", goTypesVarName(f), " = []interface{}{")
136 for _, s := range goTypes {
137 g.P(s)
138 }
139 g.P("}")
140
141 g.P("var ", depIdxsVarName(f), " = []int32{")
142 for _, s := range depIdxs {
143 g.P(s)
144 }
145 g.P("}")
146
147 g.P("func init() { ", initFuncName(f.File), "() }")
148
149 g.P("func ", initFuncName(f.File), "() {")
150 g.P("if ", f.GoDescriptorIdent, " != nil {")
151 g.P("return")
152 g.P("}")
153
154
155
156
157 for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
158 impFile := gen.FilesByPath[imps.Get(i).Path()]
159 if impFile.GoImportPath != f.GoImportPath {
160 continue
161 }
162 g.P(initFuncName(impFile), "()")
163 }
164
165 if len(f.allMessages) > 0 {
166
167 g.P("if !", protoimplPackage.Ident("UnsafeEnabled"), " {")
168 for _, message := range f.allMessages {
169 if sf := f.allMessageFieldsByPtr[message]; len(sf.unexported) > 0 {
170 idx := f.allMessagesByPtr[message]
171 typesVar := messageTypesVarName(f)
172
173 g.P(typesVar, "[", idx, "].Exporter = func(v interface{}, i int) interface{} {")
174 g.P("switch v := v.(*", message.GoIdent, "); i {")
175 for i := 0; i < sf.count; i++ {
176 if name := sf.unexported[i]; name != "" {
177 g.P("case ", i, ": return &v.", name)
178 }
179 }
180 g.P("default: return nil")
181 g.P("}")
182 g.P("}")
183 }
184 }
185 g.P("}")
186
187
188 for _, message := range f.allMessages {
189 if len(message.Oneofs) > 0 {
190 idx := f.allMessagesByPtr[message]
191 typesVar := messageTypesVarName(f)
192
193
194 g.P(typesVar, "[", idx, "].OneofWrappers = []interface{} {")
195 for _, oneof := range message.Oneofs {
196 if !oneof.Desc.IsSynthetic() {
197 for _, field := range oneof.Fields {
198 g.P("(*", field.GoIdent, ")(nil),")
199 }
200 }
201 }
202 g.P("}")
203 }
204 }
205 }
206
207 g.P("type x struct{}")
208 g.P("out := ", protoimplPackage.Ident("TypeBuilder"), "{")
209 g.P("File: ", protoimplPackage.Ident("DescBuilder"), "{")
210 g.P("GoPackagePath: ", reflectPackage.Ident("TypeOf"), "(x{}).PkgPath(),")
211 g.P("RawDescriptor: ", rawDescVarName(f), ",")
212 g.P("NumEnums: ", len(f.allEnums), ",")
213 g.P("NumMessages: ", len(f.allMessages), ",")
214 g.P("NumExtensions: ", len(f.allExtensions), ",")
215 g.P("NumServices: ", len(f.Services), ",")
216 g.P("},")
217 g.P("GoTypes: ", goTypesVarName(f), ",")
218 g.P("DependencyIndexes: ", depIdxsVarName(f), ",")
219 if len(f.allEnums) > 0 {
220 g.P("EnumInfos: ", enumTypesVarName(f), ",")
221 }
222 if len(f.allMessages) > 0 {
223 g.P("MessageInfos: ", messageTypesVarName(f), ",")
224 }
225 if len(f.allExtensions) > 0 {
226 g.P("ExtensionInfos: ", extensionTypesVarName(f), ",")
227 }
228 g.P("}.Build()")
229 g.P(f.GoDescriptorIdent, " = out.File")
230
231
232 g.P(rawDescVarName(f), " = nil")
233 g.P(goTypesVarName(f), " = nil")
234 g.P(depIdxsVarName(f), " = nil")
235 g.P("}")
236 }
237
238
239
240 func stripSourceRetentionFieldsFromMessage(m protoreflect.Message) {
241 protorange.Range(m, func(ppv protopath.Values) error {
242 m2, ok := ppv.Index(-1).Value.Interface().(protoreflect.Message)
243 if !ok {
244 return nil
245 }
246 m2.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
247 fdo, ok := fd.Options().(*descriptorpb.FieldOptions)
248 if ok && fdo.GetRetention() == descriptorpb.FieldOptions_RETENTION_SOURCE {
249 m2.Clear(fd)
250 }
251 return true
252 })
253 return nil
254 })
255 }
256
257 func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
258 descProto := proto.Clone(f.Proto).(*descriptorpb.FileDescriptorProto)
259 descProto.SourceCodeInfo = nil
260 stripSourceRetentionFieldsFromMessage(descProto.ProtoReflect())
261 b, err := proto.MarshalOptions{AllowPartial: true, Deterministic: true}.Marshal(descProto)
262 if err != nil {
263 gen.Error(err)
264 return
265 }
266
267 g.P("var ", rawDescVarName(f), " = []byte{")
268 for len(b) > 0 {
269 n := 16
270 if n > len(b) {
271 n = len(b)
272 }
273
274 s := ""
275 for _, c := range b[:n] {
276 s += fmt.Sprintf("0x%02x,", c)
277 }
278 g.P(s)
279
280 b = b[n:]
281 }
282 g.P("}")
283 g.P()
284
285 if f.needRawDesc {
286 onceVar := rawDescVarName(f) + "Once"
287 dataVar := rawDescVarName(f) + "Data"
288 g.P("var (")
289 g.P(onceVar, " ", syncPackage.Ident("Once"))
290 g.P(dataVar, " = ", rawDescVarName(f))
291 g.P(")")
292 g.P()
293
294 g.P("func ", rawDescVarName(f), "GZIP() []byte {")
295 g.P(onceVar, ".Do(func() {")
296 g.P(dataVar, " = ", protoimplPackage.Ident("X"), ".CompressGZIP(", dataVar, ")")
297 g.P("})")
298 g.P("return ", dataVar)
299 g.P("}")
300 g.P()
301 }
302 }
303
304 func genEnumReflectMethods(g *protogen.GeneratedFile, f *fileInfo, e *enumInfo) {
305 idx := f.allEnumsByPtr[e]
306 typesVar := enumTypesVarName(f)
307
308
309 g.P("func (", e.GoIdent, ") Descriptor() ", protoreflectPackage.Ident("EnumDescriptor"), " {")
310 g.P("return ", typesVar, "[", idx, "].Descriptor()")
311 g.P("}")
312 g.P()
313
314
315 g.P("func (", e.GoIdent, ") Type() ", protoreflectPackage.Ident("EnumType"), " {")
316 g.P("return &", typesVar, "[", idx, "]")
317 g.P("}")
318 g.P()
319
320
321 g.P("func (x ", e.GoIdent, ") Number() ", protoreflectPackage.Ident("EnumNumber"), " {")
322 g.P("return ", protoreflectPackage.Ident("EnumNumber"), "(x)")
323 g.P("}")
324 g.P()
325 }
326
327 func genMessageReflectMethods(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
328 idx := f.allMessagesByPtr[m]
329 typesVar := messageTypesVarName(f)
330
331
332 g.P("func (x *", m.GoIdent, ") ProtoReflect() ", protoreflectPackage.Ident("Message"), " {")
333 g.P("mi := &", typesVar, "[", idx, "]")
334 g.P("if ", protoimplPackage.Ident("UnsafeEnabled"), " && x != nil {")
335 g.P("ms := ", protoimplPackage.Ident("X"), ".MessageStateOf(", protoimplPackage.Ident("Pointer"), "(x))")
336 g.P("if ms.LoadMessageInfo() == nil {")
337 g.P("ms.StoreMessageInfo(mi)")
338 g.P("}")
339 g.P("return ms")
340 g.P("}")
341 g.P("return mi.MessageOf(x)")
342 g.P("}")
343 g.P()
344 }
345
346 func fileVarName(f *protogen.File, suffix string) string {
347 prefix := f.GoDescriptorIdent.GoName
348 _, n := utf8.DecodeRuneInString(prefix)
349 prefix = strings.ToLower(prefix[:n]) + prefix[n:]
350 return prefix + "_" + suffix
351 }
352 func rawDescVarName(f *fileInfo) string {
353 return fileVarName(f.File, "rawDesc")
354 }
355 func goTypesVarName(f *fileInfo) string {
356 return fileVarName(f.File, "goTypes")
357 }
358 func depIdxsVarName(f *fileInfo) string {
359 return fileVarName(f.File, "depIdxs")
360 }
361 func enumTypesVarName(f *fileInfo) string {
362 return fileVarName(f.File, "enumTypes")
363 }
364 func messageTypesVarName(f *fileInfo) string {
365 return fileVarName(f.File, "msgTypes")
366 }
367 func extensionTypesVarName(f *fileInfo) string {
368 return fileVarName(f.File, "extTypes")
369 }
370 func initFuncName(f *protogen.File) string {
371 return fileVarName(f, "init")
372 }
373
View as plain text