blob: c2f429ed112e16ef8db926a5fdf91827cbcdfb13 [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
12 "github.com/golang/protobuf/v2/protogen"
13 "github.com/golang/protobuf/v2/reflect/protoreflect"
14)
15
Joe Tsai24ceb2b2018-12-04 22:53:56 -080016// TODO: Remove special-casing for descriptor proto.
17func isDescriptor(f *protogen.File) bool {
18 return f.Desc.Path() == "google/protobuf/descriptor.proto" && f.Desc.Package() == "google.protobuf"
19}
Joe Tsaib6405bd2018-11-15 14:44:37 -080020
21// minimumVersion is minimum version of the v2 proto package that is required.
22// This is incremented every time the generated code relies on some property
23// in the proto package that was introduced in a later version.
24const minimumVersion = 0
25
26const (
Joe Tsai08cd8842019-03-18 13:46:39 -070027 reflectPackage = protogen.GoImportPath("reflect")
28 protoimplPackage = protogen.GoImportPath("github.com/golang/protobuf/v2/runtime/protoimpl")
29 protoreflectPackage = protogen.GoImportPath("github.com/golang/protobuf/v2/reflect/protoreflect")
30 protoregistryPackage = protogen.GoImportPath("github.com/golang/protobuf/v2/reflect/protoregistry")
31 prototypePackage = protogen.GoImportPath("github.com/golang/protobuf/v2/internal/prototype")
Joe Tsaib6405bd2018-11-15 14:44:37 -080032)
33
34// TODO: Add support for proto options.
35
Damien Neil8012b442019-01-18 09:32:24 -080036func genReflectFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
Damien Neil8012b442019-01-18 09:32:24 -080037 // Emit a static check that enforces a minimum version of the proto package.
38 // TODO: This should appear higher up in the Go source file.
39 g.P("const _ = ", protoimplPackage.Ident("EnforceVersion"), "(", protoimplPackage.Ident("Version"), " - ", minimumVersion, ")")
40
41 g.P("var ", f.GoDescriptorIdent, " ", protoreflectPackage.Ident("FileDescriptor"))
42 g.P()
43
44 if len(f.allEnums) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -080045 g.P("var ", enumTypesVarName(f), " = make([]", protoreflectPackage.Ident("EnumType"), ",", len(f.allEnums), ")")
Joe Tsaib6405bd2018-11-15 14:44:37 -080046 }
Damien Neil8012b442019-01-18 09:32:24 -080047 if len(f.allMessages) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -080048 g.P("var ", messageTypesVarName(f), " = make([]", protoimplPackage.Ident("MessageType"), ",", len(f.allMessages), ")")
Damien Neil8012b442019-01-18 09:32:24 -080049 }
50
51 // Generate a unique list of Go types for all declarations and dependencies,
52 // and the associated index into the type list for all dependencies.
53 var goTypes []string
54 var depIdxs []string
55 seen := map[protoreflect.FullName]int{}
56 genDep := func(name protoreflect.FullName, depSource string) {
57 if depSource != "" {
58 line := fmt.Sprintf("%d, // %s -> %s", seen[name], depSource, name)
59 depIdxs = append(depIdxs, line)
60 }
61 }
62 genEnum := func(e *protogen.Enum, depSource string) {
63 if e != nil {
64 name := e.Desc.FullName()
65 if _, ok := seen[name]; !ok {
66 line := fmt.Sprintf("(%s)(0), // %d: %s", g.QualifiedGoIdent(e.GoIdent), len(goTypes), name)
67 goTypes = append(goTypes, line)
68 seen[name] = len(seen)
69 }
70 if depSource != "" {
71 genDep(name, depSource)
72 }
73 }
74 }
75 genMessage := func(m *protogen.Message, depSource string) {
76 if m != nil {
77 name := m.Desc.FullName()
78 if _, ok := seen[name]; !ok {
79 line := fmt.Sprintf("(*%s)(nil), // %d: %s", g.QualifiedGoIdent(m.GoIdent), len(goTypes), name)
80 if m.Desc.IsMapEntry() {
81 // Map entry messages have no associated Go type.
82 line = fmt.Sprintf("nil, // %d: %s", len(goTypes), name)
83 }
84 goTypes = append(goTypes, line)
85 seen[name] = len(seen)
86 }
87 if depSource != "" {
88 genDep(name, depSource)
89 }
90 }
91 }
92
93 // This ordering is significant. See protoimpl.FileBuilder.GoTypes.
94 for _, enum := range f.allEnums {
95 genEnum(enum, "")
96 }
97 for _, message := range f.allMessages {
98 genMessage(message, "")
99 }
100 for _, extension := range f.allExtensions {
101 source := string(extension.Desc.FullName())
102 genMessage(extension.ExtendedType, source+":extendee")
103 }
104 for _, message := range f.allMessages {
105 for _, field := range message.Fields {
106 if field.Desc.IsWeak() {
107 continue
108 }
109 source := string(field.Desc.FullName())
110 genEnum(field.EnumType, source+":type_name")
111 genMessage(field.MessageType, source+":type_name")
112 }
113 }
114 for _, extension := range f.allExtensions {
115 source := string(extension.Desc.FullName())
116 genEnum(extension.EnumType, source+":type_name")
117 genMessage(extension.MessageType, source+":type_name")
118 }
119 for _, service := range f.Services {
120 for _, method := range service.Methods {
121 source := string(method.Desc.FullName())
122 genMessage(method.InputType, source+":input_type")
123 genMessage(method.OutputType, source+":output_type")
124 }
125 }
126 if len(depIdxs) > math.MaxInt32 {
127 panic("too many dependencies") // sanity check
128 }
129
130 g.P("var ", goTypesVarName(f), " = []interface{}{")
131 for _, s := range goTypes {
132 g.P(s)
133 }
134 g.P("}")
135
136 g.P("var ", depIdxsVarName(f), " = []int32{")
137 for _, s := range depIdxs {
138 g.P(s)
139 }
140 g.P("}")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800141
Damien Neil0fc22452019-03-08 17:18:11 -0800142 g.P("func init() { ", initFuncName(f.File), "() }")
143
144 g.P("func ", initFuncName(f.File), "() {")
145 g.P("if ", f.GoDescriptorIdent, " != nil {")
146 g.P("return")
147 g.P("}")
148
149 // Ensure that initialization functions for different files in the same Go
150 // package run in the correct order: Call the init funcs for every .proto file
151 // imported by this one that is in the same Go package.
152 for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
153 impFile, _ := gen.FileByName(imps.Get(i).Path())
154 if impFile.GoImportPath != f.GoImportPath {
155 continue
156 }
157 g.P(initFuncName(impFile), "()")
158 }
159
Damien Neil8012b442019-01-18 09:32:24 -0800160 if len(f.allMessages) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800161 g.P("messageTypes := make([]", protoreflectPackage.Ident("MessageType"), ",", len(f.allMessages), ")")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800162 }
Damien Neil8012b442019-01-18 09:32:24 -0800163 if len(f.allExtensions) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800164 g.P("extensionTypes := make([]", protoreflectPackage.Ident("ExtensionType"), ",", len(f.allExtensions), ")")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800165 }
166
Damien Neil8012b442019-01-18 09:32:24 -0800167 g.P(f.GoDescriptorIdent, " = ", protoimplPackage.Ident("FileBuilder"), "{")
168 g.P("RawDescriptor: ", f.descriptorRawVar, ",")
169 g.P("GoTypes: ", goTypesVarName(f), ",")
170 g.P("DependencyIndexes: ", depIdxsVarName(f), ",")
Joe Tsaiafb455e2019-03-14 16:08:22 -0700171 if len(f.allExtensions) > 0 {
172 g.P("LegacyExtensions: ", extDecsVarName(f), ",")
173 }
Damien Neil8012b442019-01-18 09:32:24 -0800174 if len(f.allEnums) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800175 g.P("EnumOutputTypes: ", enumTypesVarName(f), ",")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800176 }
Damien Neil8012b442019-01-18 09:32:24 -0800177 if len(f.allMessages) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800178 g.P("MessageOutputTypes: messageTypes,")
Damien Neil8012b442019-01-18 09:32:24 -0800179 }
180 if len(f.allExtensions) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800181 g.P("ExtensionOutputTypes: extensionTypes,")
Damien Neil8012b442019-01-18 09:32:24 -0800182 }
Joe Tsai08cd8842019-03-18 13:46:39 -0700183 if isDescriptor(f.File) {
184 // TODO: Enable this for all protos.
185 g.P("FilesRegistry: ", protoregistryPackage.Ident("GlobalFiles"), ",")
186 g.P("TypesRegistry: ", protoregistryPackage.Ident("GlobalTypes"), ",")
187 }
Damien Neil8012b442019-01-18 09:32:24 -0800188 g.P("}.Init()")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800189
Damien Neil8012b442019-01-18 09:32:24 -0800190 // Copy the local list of message types into the global array.
191 if len(f.allMessages) > 0 {
192 g.P("messageGoTypes := ", goTypesVarName(f), "[", len(f.allEnums), ":][:", len(f.allMessages), "]")
Damien Neil6bb8dec2019-03-01 13:22:30 -0800193 g.P("for i, mt := range messageTypes {")
Damien Neil8012b442019-01-18 09:32:24 -0800194 g.P(messageTypesVarName(f), "[i].GoType = ", reflectPackage.Ident("TypeOf"), "(messageGoTypes[i])")
195 g.P(messageTypesVarName(f), "[i].PBType = mt")
196 g.P("}")
197 }
198
Joe Tsaia4cbffe2018-12-06 13:01:52 -0800199 // The descriptor proto needs to register the option types with the
200 // prototype so that the package can properly handle those option types.
201 if isDescriptor(f.File) {
202 for _, m := range f.allMessages {
203 name := m.GoIdent.GoName
204 if strings.HasSuffix(name, "Options") {
205 g.P(prototypePackage.Ident("X"), ".Register", name, "((*", name, ")(nil))")
206 }
207 }
208 }
209
Joe Tsai559d47f2019-03-19 18:21:47 -0700210 genRegistrationV1(gen, g, f)
211
Damien Neil8012b442019-01-18 09:32:24 -0800212 g.P(goTypesVarName(f), " = nil") // allow GC to reclaim resource
213 g.P(depIdxsVarName(f), " = nil") // allow GC to reclaim resource
Joe Tsaib6405bd2018-11-15 14:44:37 -0800214 g.P("}")
215}
216
Joe Tsaib6405bd2018-11-15 14:44:37 -0800217func genReflectEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, enum *protogen.Enum) {
Joe Tsai9667c482018-12-05 15:42:52 -0800218 idx := f.allEnumsByPtr[enum]
Joe Tsaib6405bd2018-11-15 14:44:37 -0800219 typesVar := enumTypesVarName(f)
Damien Neila8593ba2019-01-08 16:18:07 -0800220 g.P("func (e ", enum.GoIdent, ") Type() ", protoreflectPackage.Ident("EnumType"), " {")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800221 g.P("return ", typesVar, "[", idx, "]")
222 g.P("}")
Damien Neila8593ba2019-01-08 16:18:07 -0800223 g.P("func (e ", enum.GoIdent, ") Number() ", protoreflectPackage.Ident("EnumNumber"), " {")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800224 g.P("return ", protoreflectPackage.Ident("EnumNumber"), "(e)")
225 g.P("}")
226}
227
228func genReflectMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message) {
Joe Tsai9667c482018-12-05 15:42:52 -0800229 idx := f.allMessagesByPtr[message]
Joe Tsaib6405bd2018-11-15 14:44:37 -0800230 typesVar := messageTypesVarName(f)
231 g.P("func (m *", message.GoIdent, ") ProtoReflect() ", protoreflectPackage.Ident("Message"), " {")
Damien Neil8012b442019-01-18 09:32:24 -0800232 g.P("return ", typesVar, "[", idx, "].MessageOf(m)")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800233 g.P("}")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800234}
235
Damien Neil8012b442019-01-18 09:32:24 -0800236func goTypesVarName(f *fileInfo) string {
237 return "xxx_" + f.GoDescriptorIdent.GoName + "_goTypes"
238}
239func depIdxsVarName(f *fileInfo) string {
240 return "xxx_" + f.GoDescriptorIdent.GoName + "_depIdxs"
Joe Tsaib6405bd2018-11-15 14:44:37 -0800241}
242func enumTypesVarName(f *fileInfo) string {
Damien Neil8012b442019-01-18 09:32:24 -0800243 return "xxx_" + f.GoDescriptorIdent.GoName + "_enumTypes"
Joe Tsaib6405bd2018-11-15 14:44:37 -0800244}
245func messageTypesVarName(f *fileInfo) string {
Damien Neil8012b442019-01-18 09:32:24 -0800246 return "xxx_" + f.GoDescriptorIdent.GoName + "_messageTypes"
Joe Tsaib6405bd2018-11-15 14:44:37 -0800247}
Joe Tsaiafb455e2019-03-14 16:08:22 -0700248func extDecsVarName(f *fileInfo) string {
249 return "xxx_" + f.GoDescriptorIdent.GoName + "_extDescs"
250}
Damien Neil0fc22452019-03-08 17:18:11 -0800251func initFuncName(f *protogen.File) string {
252 return "xxx_" + f.GoDescriptorIdent.GoName + "_init"
253}