blob: 2d0a9ff5c7bf100108bb01625630bd871e8e8908 [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 "strings"
11
Joe Tsai5d72cc22019-03-28 01:13:26 -070012 "github.com/golang/protobuf/v2/proto"
Joe Tsaib6405bd2018-11-15 14:44:37 -080013 "github.com/golang/protobuf/v2/protogen"
14 "github.com/golang/protobuf/v2/reflect/protoreflect"
Joe Tsai5d72cc22019-03-28 01:13:26 -070015
16 descriptorpb "github.com/golang/protobuf/v2/types/descriptor"
Joe Tsaib6405bd2018-11-15 14:44:37 -080017)
18
Joe Tsaib6405bd2018-11-15 14:44:37 -080019// TODO: Add support for proto options.
20
Damien Neil8012b442019-01-18 09:32:24 -080021func genReflectFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
Damien Neil8012b442019-01-18 09:32:24 -080022 g.P("var ", f.GoDescriptorIdent, " ", protoreflectPackage.Ident("FileDescriptor"))
23 g.P()
24
Joe Tsai5d72cc22019-03-28 01:13:26 -070025 genFileDescriptor(gen, g, f)
Damien Neil8012b442019-01-18 09:32:24 -080026 if len(f.allEnums) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -080027 g.P("var ", enumTypesVarName(f), " = make([]", protoreflectPackage.Ident("EnumType"), ",", len(f.allEnums), ")")
Joe Tsaib6405bd2018-11-15 14:44:37 -080028 }
Damien Neil8012b442019-01-18 09:32:24 -080029 if len(f.allMessages) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -080030 g.P("var ", messageTypesVarName(f), " = make([]", protoimplPackage.Ident("MessageType"), ",", len(f.allMessages), ")")
Damien Neil8012b442019-01-18 09:32:24 -080031 }
32
33 // Generate a unique list of Go types for all declarations and dependencies,
34 // and the associated index into the type list for all dependencies.
35 var goTypes []string
36 var depIdxs []string
37 seen := map[protoreflect.FullName]int{}
38 genDep := func(name protoreflect.FullName, depSource string) {
39 if depSource != "" {
40 line := fmt.Sprintf("%d, // %s -> %s", seen[name], depSource, name)
41 depIdxs = append(depIdxs, line)
42 }
43 }
44 genEnum := func(e *protogen.Enum, depSource string) {
45 if e != nil {
46 name := e.Desc.FullName()
47 if _, ok := seen[name]; !ok {
48 line := fmt.Sprintf("(%s)(0), // %d: %s", g.QualifiedGoIdent(e.GoIdent), len(goTypes), name)
49 goTypes = append(goTypes, line)
50 seen[name] = len(seen)
51 }
52 if depSource != "" {
53 genDep(name, depSource)
54 }
55 }
56 }
57 genMessage := func(m *protogen.Message, depSource string) {
58 if m != nil {
59 name := m.Desc.FullName()
60 if _, ok := seen[name]; !ok {
61 line := fmt.Sprintf("(*%s)(nil), // %d: %s", g.QualifiedGoIdent(m.GoIdent), len(goTypes), name)
62 if m.Desc.IsMapEntry() {
63 // Map entry messages have no associated Go type.
64 line = fmt.Sprintf("nil, // %d: %s", len(goTypes), name)
65 }
66 goTypes = append(goTypes, line)
67 seen[name] = len(seen)
68 }
69 if depSource != "" {
70 genDep(name, depSource)
71 }
72 }
73 }
74
75 // This ordering is significant. See protoimpl.FileBuilder.GoTypes.
76 for _, enum := range f.allEnums {
77 genEnum(enum, "")
78 }
79 for _, message := range f.allMessages {
80 genMessage(message, "")
81 }
82 for _, extension := range f.allExtensions {
83 source := string(extension.Desc.FullName())
84 genMessage(extension.ExtendedType, source+":extendee")
85 }
86 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())
92 genEnum(field.EnumType, source+":type_name")
93 genMessage(field.MessageType, source+":type_name")
94 }
95 }
96 for _, extension := range f.allExtensions {
97 source := string(extension.Desc.FullName())
98 genEnum(extension.EnumType, source+":type_name")
99 genMessage(extension.MessageType, source+":type_name")
100 }
101 for _, service := range f.Services {
102 for _, method := range service.Methods {
103 source := string(method.Desc.FullName())
104 genMessage(method.InputType, source+":input_type")
105 genMessage(method.OutputType, source+":output_type")
106 }
107 }
108 if len(depIdxs) > math.MaxInt32 {
109 panic("too many dependencies") // sanity check
110 }
111
112 g.P("var ", goTypesVarName(f), " = []interface{}{")
113 for _, s := range goTypes {
114 g.P(s)
115 }
116 g.P("}")
117
118 g.P("var ", depIdxsVarName(f), " = []int32{")
119 for _, s := range depIdxs {
120 g.P(s)
121 }
122 g.P("}")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800123
Damien Neil0fc22452019-03-08 17:18:11 -0800124 g.P("func init() { ", initFuncName(f.File), "() }")
125
126 g.P("func ", initFuncName(f.File), "() {")
127 g.P("if ", f.GoDescriptorIdent, " != nil {")
128 g.P("return")
129 g.P("}")
130
131 // Ensure that initialization functions for different files in the same Go
132 // package run in the correct order: Call the init funcs for every .proto file
133 // imported by this one that is in the same Go package.
134 for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
135 impFile, _ := gen.FileByName(imps.Get(i).Path())
136 if impFile.GoImportPath != f.GoImportPath {
137 continue
138 }
139 g.P(initFuncName(impFile), "()")
140 }
141
Damien Neil8012b442019-01-18 09:32:24 -0800142 if len(f.allExtensions) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800143 g.P("extensionTypes := make([]", protoreflectPackage.Ident("ExtensionType"), ",", len(f.allExtensions), ")")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800144 }
145
Damien Neil8012b442019-01-18 09:32:24 -0800146 g.P(f.GoDescriptorIdent, " = ", protoimplPackage.Ident("FileBuilder"), "{")
Joe Tsai5d72cc22019-03-28 01:13:26 -0700147 g.P("RawDescriptor: ", rawDescVarName(f), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800148 g.P("GoTypes: ", goTypesVarName(f), ",")
149 g.P("DependencyIndexes: ", depIdxsVarName(f), ",")
Joe Tsaiafb455e2019-03-14 16:08:22 -0700150 if len(f.allExtensions) > 0 {
151 g.P("LegacyExtensions: ", extDecsVarName(f), ",")
152 }
Damien Neil8012b442019-01-18 09:32:24 -0800153 if len(f.allEnums) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800154 g.P("EnumOutputTypes: ", enumTypesVarName(f), ",")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800155 }
Damien Neil8012b442019-01-18 09:32:24 -0800156 if len(f.allMessages) > 0 {
Joe Tsai35ec98f2019-03-25 14:41:32 -0700157 g.P("MessageOutputTypes: ", messageTypesVarName(f), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800158 }
159 if len(f.allExtensions) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800160 g.P("ExtensionOutputTypes: extensionTypes,")
Damien Neil8012b442019-01-18 09:32:24 -0800161 }
Joe Tsai35ec98f2019-03-25 14:41:32 -0700162 g.P("FilesRegistry: ", protoregistryPackage.Ident("GlobalFiles"), ",")
163 g.P("TypesRegistry: ", protoregistryPackage.Ident("GlobalTypes"), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800164 g.P("}.Init()")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800165
Joe Tsaia4cbffe2018-12-06 13:01:52 -0800166 // The descriptor proto needs to register the option types with the
167 // prototype so that the package can properly handle those option types.
Joe Tsai35ec98f2019-03-25 14:41:32 -0700168 //
169 // TODO: Should this be handled by fileinit at runtime?
170 if f.Desc.Path() == "google/protobuf/descriptor.proto" && f.Desc.Package() == "google.protobuf" {
Joe Tsaia4cbffe2018-12-06 13:01:52 -0800171 for _, m := range f.allMessages {
172 name := m.GoIdent.GoName
173 if strings.HasSuffix(name, "Options") {
174 g.P(prototypePackage.Ident("X"), ".Register", name, "((*", name, ")(nil))")
175 }
176 }
177 }
178
Joe Tsai5d72cc22019-03-28 01:13:26 -0700179 // Set inputs to nil to allow GC to reclaim resources.
180 g.P(rawDescVarName(f), " = nil")
181 g.P(goTypesVarName(f), " = nil")
182 g.P(depIdxsVarName(f), " = nil")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800183 g.P("}")
184}
185
Joe Tsai5d72cc22019-03-28 01:13:26 -0700186func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
187 // TODO: Replace this with v2 Clone.
188 descProto := new(descriptorpb.FileDescriptorProto)
189 b, err := proto.Marshal(f.Proto)
190 if err != nil {
191 gen.Error(err)
192 return
193 }
194 if err := proto.Unmarshal(b, descProto); err != nil {
195 gen.Error(err)
196 return
197 }
198
199 // Trim the source_code_info from the descriptor.
200 descProto.SourceCodeInfo = nil
201 b, err = proto.MarshalOptions{Deterministic: true}.Marshal(descProto)
202 if err != nil {
203 gen.Error(err)
204 return
205 }
206
207 g.P("var ", rawDescVarName(f), " = []byte{")
208 for len(b) > 0 {
209 n := 16
210 if n > len(b) {
211 n = len(b)
212 }
213
214 s := ""
215 for _, c := range b[:n] {
216 s += fmt.Sprintf("0x%02x,", c)
217 }
218 g.P(s)
219
220 b = b[n:]
221 }
222 g.P("}")
223 g.P()
224
225 onceVar := rawDescVarName(f) + "_once"
226 dataVar := rawDescVarName(f) + "_data"
227 g.P("var (")
228 g.P(onceVar, " ", syncPackage.Ident("Once"))
229 g.P(dataVar, " = ", rawDescVarName(f))
230 g.P(")")
231 g.P()
232
233 g.P("func ", rawDescVarName(f), "GZIP() []byte {")
234 g.P(onceVar, ".Do(func() {")
235 g.P(dataVar, " = ", protoimplPackage.Ident("X"), ".CompressGZIP(", dataVar, ")")
236 g.P("})")
237 g.P("return ", dataVar)
238 g.P("}")
239 g.P()
240}
241
Joe Tsaib6405bd2018-11-15 14:44:37 -0800242func genReflectEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, enum *protogen.Enum) {
Joe Tsai9667c482018-12-05 15:42:52 -0800243 idx := f.allEnumsByPtr[enum]
Joe Tsaib6405bd2018-11-15 14:44:37 -0800244 typesVar := enumTypesVarName(f)
Joe Tsai61968ce2019-04-01 12:59:24 -0700245
246 // Type method.
247 g.P("func (", enum.GoIdent, ") Type() ", protoreflectPackage.Ident("EnumType"), " {")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800248 g.P("return ", typesVar, "[", idx, "]")
249 g.P("}")
Joe Tsai61968ce2019-04-01 12:59:24 -0700250 g.P()
251
252 // Number method.
253 g.P("func (x ", enum.GoIdent, ") Number() ", protoreflectPackage.Ident("EnumNumber"), " {")
254 g.P("return ", protoreflectPackage.Ident("EnumNumber"), "(x)")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800255 g.P("}")
Joe Tsai61968ce2019-04-01 12:59:24 -0700256 g.P()
Joe Tsaib6405bd2018-11-15 14:44:37 -0800257}
258
259func genReflectMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message) {
Joe Tsai9667c482018-12-05 15:42:52 -0800260 idx := f.allMessagesByPtr[message]
Joe Tsaib6405bd2018-11-15 14:44:37 -0800261 typesVar := messageTypesVarName(f)
Joe Tsai61968ce2019-04-01 12:59:24 -0700262
263 // ProtoReflect method.
264 g.P("func (x *", message.GoIdent, ") ProtoReflect() ", protoreflectPackage.Ident("Message"), " {")
265 g.P("return ", typesVar, "[", idx, "].MessageOf(x)")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800266 g.P("}")
Joe Tsai61968ce2019-04-01 12:59:24 -0700267 g.P()
Joe Tsaib6405bd2018-11-15 14:44:37 -0800268}
269
Joe Tsai5d72cc22019-03-28 01:13:26 -0700270func rawDescVarName(f *fileInfo) string {
271 return "xxx_" + f.GoDescriptorIdent.GoName + "_rawDesc"
272}
Damien Neil8012b442019-01-18 09:32:24 -0800273func goTypesVarName(f *fileInfo) string {
274 return "xxx_" + f.GoDescriptorIdent.GoName + "_goTypes"
275}
276func depIdxsVarName(f *fileInfo) string {
277 return "xxx_" + f.GoDescriptorIdent.GoName + "_depIdxs"
Joe Tsaib6405bd2018-11-15 14:44:37 -0800278}
279func enumTypesVarName(f *fileInfo) string {
Damien Neil8012b442019-01-18 09:32:24 -0800280 return "xxx_" + f.GoDescriptorIdent.GoName + "_enumTypes"
Joe Tsaib6405bd2018-11-15 14:44:37 -0800281}
282func messageTypesVarName(f *fileInfo) string {
Damien Neil8012b442019-01-18 09:32:24 -0800283 return "xxx_" + f.GoDescriptorIdent.GoName + "_messageTypes"
Joe Tsaib6405bd2018-11-15 14:44:37 -0800284}
Joe Tsaiafb455e2019-03-14 16:08:22 -0700285func extDecsVarName(f *fileInfo) string {
286 return "xxx_" + f.GoDescriptorIdent.GoName + "_extDescs"
287}
Damien Neil0fc22452019-03-08 17:18:11 -0800288func initFuncName(f *protogen.File) string {
289 return "xxx_" + f.GoDescriptorIdent.GoName + "_init"
290}