blob: cf020788f630cf56731da303af0963e79dfc241a [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
Joe Tsaib6405bd2018-11-15 14:44:37 -080026// TODO: Add support for proto options.
27
Damien Neil8012b442019-01-18 09:32:24 -080028func genReflectFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
Damien Neil8012b442019-01-18 09:32:24 -080029 // Emit a static check that enforces a minimum version of the proto package.
30 // TODO: This should appear higher up in the Go source file.
31 g.P("const _ = ", protoimplPackage.Ident("EnforceVersion"), "(", protoimplPackage.Ident("Version"), " - ", minimumVersion, ")")
32
33 g.P("var ", f.GoDescriptorIdent, " ", protoreflectPackage.Ident("FileDescriptor"))
34 g.P()
35
36 if len(f.allEnums) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -080037 g.P("var ", enumTypesVarName(f), " = make([]", protoreflectPackage.Ident("EnumType"), ",", len(f.allEnums), ")")
Joe Tsaib6405bd2018-11-15 14:44:37 -080038 }
Damien Neil8012b442019-01-18 09:32:24 -080039 if len(f.allMessages) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -080040 g.P("var ", messageTypesVarName(f), " = make([]", protoimplPackage.Ident("MessageType"), ",", len(f.allMessages), ")")
Damien Neil8012b442019-01-18 09:32:24 -080041 }
42
43 // Generate a unique list of Go types for all declarations and dependencies,
44 // and the associated index into the type list for all dependencies.
45 var goTypes []string
46 var depIdxs []string
47 seen := map[protoreflect.FullName]int{}
48 genDep := func(name protoreflect.FullName, depSource string) {
49 if depSource != "" {
50 line := fmt.Sprintf("%d, // %s -> %s", seen[name], depSource, name)
51 depIdxs = append(depIdxs, line)
52 }
53 }
54 genEnum := func(e *protogen.Enum, depSource string) {
55 if e != nil {
56 name := e.Desc.FullName()
57 if _, ok := seen[name]; !ok {
58 line := fmt.Sprintf("(%s)(0), // %d: %s", g.QualifiedGoIdent(e.GoIdent), len(goTypes), name)
59 goTypes = append(goTypes, line)
60 seen[name] = len(seen)
61 }
62 if depSource != "" {
63 genDep(name, depSource)
64 }
65 }
66 }
67 genMessage := func(m *protogen.Message, depSource string) {
68 if m != nil {
69 name := m.Desc.FullName()
70 if _, ok := seen[name]; !ok {
71 line := fmt.Sprintf("(*%s)(nil), // %d: %s", g.QualifiedGoIdent(m.GoIdent), len(goTypes), name)
72 if m.Desc.IsMapEntry() {
73 // Map entry messages have no associated Go type.
74 line = fmt.Sprintf("nil, // %d: %s", len(goTypes), name)
75 }
76 goTypes = append(goTypes, line)
77 seen[name] = len(seen)
78 }
79 if depSource != "" {
80 genDep(name, depSource)
81 }
82 }
83 }
84
85 // This ordering is significant. See protoimpl.FileBuilder.GoTypes.
86 for _, enum := range f.allEnums {
87 genEnum(enum, "")
88 }
89 for _, message := range f.allMessages {
90 genMessage(message, "")
91 }
92 for _, extension := range f.allExtensions {
93 source := string(extension.Desc.FullName())
94 genMessage(extension.ExtendedType, source+":extendee")
95 }
96 for _, message := range f.allMessages {
97 for _, field := range message.Fields {
98 if field.Desc.IsWeak() {
99 continue
100 }
101 source := string(field.Desc.FullName())
102 genEnum(field.EnumType, source+":type_name")
103 genMessage(field.MessageType, source+":type_name")
104 }
105 }
106 for _, extension := range f.allExtensions {
107 source := string(extension.Desc.FullName())
108 genEnum(extension.EnumType, source+":type_name")
109 genMessage(extension.MessageType, source+":type_name")
110 }
111 for _, service := range f.Services {
112 for _, method := range service.Methods {
113 source := string(method.Desc.FullName())
114 genMessage(method.InputType, source+":input_type")
115 genMessage(method.OutputType, source+":output_type")
116 }
117 }
118 if len(depIdxs) > math.MaxInt32 {
119 panic("too many dependencies") // sanity check
120 }
121
122 g.P("var ", goTypesVarName(f), " = []interface{}{")
123 for _, s := range goTypes {
124 g.P(s)
125 }
126 g.P("}")
127
128 g.P("var ", depIdxsVarName(f), " = []int32{")
129 for _, s := range depIdxs {
130 g.P(s)
131 }
132 g.P("}")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800133
Damien Neil0fc22452019-03-08 17:18:11 -0800134 g.P("func init() { ", initFuncName(f.File), "() }")
135
136 g.P("func ", initFuncName(f.File), "() {")
137 g.P("if ", f.GoDescriptorIdent, " != nil {")
138 g.P("return")
139 g.P("}")
140
141 // Ensure that initialization functions for different files in the same Go
142 // package run in the correct order: Call the init funcs for every .proto file
143 // imported by this one that is in the same Go package.
144 for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
145 impFile, _ := gen.FileByName(imps.Get(i).Path())
146 if impFile.GoImportPath != f.GoImportPath {
147 continue
148 }
149 g.P(initFuncName(impFile), "()")
150 }
151
Damien Neil8012b442019-01-18 09:32:24 -0800152 if len(f.allExtensions) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800153 g.P("extensionTypes := make([]", protoreflectPackage.Ident("ExtensionType"), ",", len(f.allExtensions), ")")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800154 }
155
Damien Neil8012b442019-01-18 09:32:24 -0800156 g.P(f.GoDescriptorIdent, " = ", protoimplPackage.Ident("FileBuilder"), "{")
157 g.P("RawDescriptor: ", f.descriptorRawVar, ",")
158 g.P("GoTypes: ", goTypesVarName(f), ",")
159 g.P("DependencyIndexes: ", depIdxsVarName(f), ",")
Joe Tsaiafb455e2019-03-14 16:08:22 -0700160 if len(f.allExtensions) > 0 {
161 g.P("LegacyExtensions: ", extDecsVarName(f), ",")
162 }
Damien Neil8012b442019-01-18 09:32:24 -0800163 if len(f.allEnums) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800164 g.P("EnumOutputTypes: ", enumTypesVarName(f), ",")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800165 }
Damien Neil8012b442019-01-18 09:32:24 -0800166 if len(f.allMessages) > 0 {
Joe Tsai35ec98f2019-03-25 14:41:32 -0700167 g.P("MessageOutputTypes: ", messageTypesVarName(f), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800168 }
169 if len(f.allExtensions) > 0 {
Damien Neil6bb8dec2019-03-01 13:22:30 -0800170 g.P("ExtensionOutputTypes: extensionTypes,")
Damien Neil8012b442019-01-18 09:32:24 -0800171 }
Joe Tsai35ec98f2019-03-25 14:41:32 -0700172 g.P("FilesRegistry: ", protoregistryPackage.Ident("GlobalFiles"), ",")
173 g.P("TypesRegistry: ", protoregistryPackage.Ident("GlobalTypes"), ",")
Damien Neil8012b442019-01-18 09:32:24 -0800174 g.P("}.Init()")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800175
Joe Tsaia4cbffe2018-12-06 13:01:52 -0800176 // The descriptor proto needs to register the option types with the
177 // prototype so that the package can properly handle those option types.
Joe Tsai35ec98f2019-03-25 14:41:32 -0700178 //
179 // TODO: Should this be handled by fileinit at runtime?
180 if f.Desc.Path() == "google/protobuf/descriptor.proto" && f.Desc.Package() == "google.protobuf" {
Joe Tsaia4cbffe2018-12-06 13:01:52 -0800181 for _, m := range f.allMessages {
182 name := m.GoIdent.GoName
183 if strings.HasSuffix(name, "Options") {
184 g.P(prototypePackage.Ident("X"), ".Register", name, "((*", name, ")(nil))")
185 }
186 }
187 }
188
Damien Neil8012b442019-01-18 09:32:24 -0800189 g.P(goTypesVarName(f), " = nil") // allow GC to reclaim resource
190 g.P(depIdxsVarName(f), " = nil") // allow GC to reclaim resource
Joe Tsaib6405bd2018-11-15 14:44:37 -0800191 g.P("}")
192}
193
Joe Tsaib6405bd2018-11-15 14:44:37 -0800194func genReflectEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, enum *protogen.Enum) {
Joe Tsai9667c482018-12-05 15:42:52 -0800195 idx := f.allEnumsByPtr[enum]
Joe Tsaib6405bd2018-11-15 14:44:37 -0800196 typesVar := enumTypesVarName(f)
Damien Neila8593ba2019-01-08 16:18:07 -0800197 g.P("func (e ", enum.GoIdent, ") Type() ", protoreflectPackage.Ident("EnumType"), " {")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800198 g.P("return ", typesVar, "[", idx, "]")
199 g.P("}")
Damien Neila8593ba2019-01-08 16:18:07 -0800200 g.P("func (e ", enum.GoIdent, ") Number() ", protoreflectPackage.Ident("EnumNumber"), " {")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800201 g.P("return ", protoreflectPackage.Ident("EnumNumber"), "(e)")
202 g.P("}")
203}
204
205func genReflectMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message) {
Joe Tsai9667c482018-12-05 15:42:52 -0800206 idx := f.allMessagesByPtr[message]
Joe Tsaib6405bd2018-11-15 14:44:37 -0800207 typesVar := messageTypesVarName(f)
208 g.P("func (m *", message.GoIdent, ") ProtoReflect() ", protoreflectPackage.Ident("Message"), " {")
Damien Neil8012b442019-01-18 09:32:24 -0800209 g.P("return ", typesVar, "[", idx, "].MessageOf(m)")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800210 g.P("}")
Joe Tsaib6405bd2018-11-15 14:44:37 -0800211}
212
Damien Neil8012b442019-01-18 09:32:24 -0800213func goTypesVarName(f *fileInfo) string {
214 return "xxx_" + f.GoDescriptorIdent.GoName + "_goTypes"
215}
216func depIdxsVarName(f *fileInfo) string {
217 return "xxx_" + f.GoDescriptorIdent.GoName + "_depIdxs"
Joe Tsaib6405bd2018-11-15 14:44:37 -0800218}
219func enumTypesVarName(f *fileInfo) string {
Damien Neil8012b442019-01-18 09:32:24 -0800220 return "xxx_" + f.GoDescriptorIdent.GoName + "_enumTypes"
Joe Tsaib6405bd2018-11-15 14:44:37 -0800221}
222func messageTypesVarName(f *fileInfo) string {
Damien Neil8012b442019-01-18 09:32:24 -0800223 return "xxx_" + f.GoDescriptorIdent.GoName + "_messageTypes"
Joe Tsaib6405bd2018-11-15 14:44:37 -0800224}
Joe Tsaiafb455e2019-03-14 16:08:22 -0700225func extDecsVarName(f *fileInfo) string {
226 return "xxx_" + f.GoDescriptorIdent.GoName + "_extDescs"
227}
Damien Neil0fc22452019-03-08 17:18:11 -0800228func initFuncName(f *protogen.File) string {
229 return "xxx_" + f.GoDescriptorIdent.GoName + "_init"
230}