1// Copyright 2019 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 5// Package filetype provides functionality for wrapping descriptors 6// with Go type information. 7package filetype 8 9import ( 10 "reflect" 11 12 "google.golang.org/protobuf/internal/descopts" 13 "google.golang.org/protobuf/internal/filedesc" 14 pimpl "google.golang.org/protobuf/internal/impl" 15 "google.golang.org/protobuf/reflect/protoreflect" 16 "google.golang.org/protobuf/reflect/protoregistry" 17) 18 19// Builder constructs type descriptors from a raw file descriptor 20// and associated Go types for each enum and message declaration. 21// 22// # Flattened Ordering 23// 24// The protobuf type system represents declarations as a tree. Certain nodes in 25// the tree require us to either associate it with a concrete Go type or to 26// resolve a dependency, which is information that must be provided separately 27// since it cannot be derived from the file descriptor alone. 28// 29// However, representing a tree as Go literals is difficult to simply do in a 30// space and time efficient way. Thus, we store them as a flattened list of 31// objects where the serialization order from the tree-based form is important. 32// 33// The "flattened ordering" is defined as a tree traversal of all enum, message, 34// extension, and service declarations using the following algorithm: 35// 36// def VisitFileDecls(fd): 37// for e in fd.Enums: yield e 38// for m in fd.Messages: yield m 39// for x in fd.Extensions: yield x 40// for s in fd.Services: yield s 41// for m in fd.Messages: yield from VisitMessageDecls(m) 42// 43// def VisitMessageDecls(md): 44// for e in md.Enums: yield e 45// for m in md.Messages: yield m 46// for x in md.Extensions: yield x 47// for m in md.Messages: yield from VisitMessageDecls(m) 48// 49// The traversal starts at the root file descriptor and yields each direct 50// declaration within each node before traversing into sub-declarations 51// that children themselves may have. 52type Builder struct { 53 // File is the underlying file descriptor builder. 54 File filedesc.Builder 55 56 // GoTypes is a unique set of the Go types for all declarations and 57 // dependencies. Each type is represented as a zero value of the Go type. 58 // 59 // Declarations are Go types generated for enums and messages directly 60 // declared (not publicly imported) in the proto source file. 61 // Messages for map entries are accounted for, but represented by nil. 62 // Enum declarations in "flattened ordering" come first, followed by 63 // message declarations in "flattened ordering". 64 // 65 // Dependencies are Go types for enums or messages referenced by 66 // message fields (excluding weak fields), for parent extended messages of 67 // extension fields, for enums or messages referenced by extension fields, 68 // and for input and output messages referenced by service methods. 69 // Dependencies must come after declarations, but the ordering of 70 // dependencies themselves is unspecified. 71 GoTypes []interface{} 72 73 // DependencyIndexes is an ordered list of indexes into GoTypes for the 74 // dependencies of messages, extensions, or services. 75 // 76 // There are 5 sub-lists in "flattened ordering" concatenated back-to-back: 77 // 0. Message field dependencies: list of the enum or message type 78 // referred to by every message field. 79 // 1. Extension field targets: list of the extended parent message of 80 // every extension. 81 // 2. Extension field dependencies: list of the enum or message type 82 // referred to by every extension field. 83 // 3. Service method inputs: list of the input message type 84 // referred to by every service method. 85 // 4. Service method outputs: list of the output message type 86 // referred to by every service method. 87 // 88 // The offset into DependencyIndexes for the start of each sub-list 89 // is appended to the end in reverse order. 90 DependencyIndexes []int32 91 92 // EnumInfos is a list of enum infos in "flattened ordering". 93 EnumInfos []pimpl.EnumInfo 94 95 // MessageInfos is a list of message infos in "flattened ordering". 96 // If provided, the GoType and PBType for each element is populated. 97 // 98 // Requirement: len(MessageInfos) == len(Build.Messages) 99 MessageInfos []pimpl.MessageInfo 100 101 // ExtensionInfos is a list of extension infos in "flattened ordering". 102 // Each element is initialized and registered with the protoregistry package. 103 // 104 // Requirement: len(LegacyExtensions) == len(Build.Extensions) 105 ExtensionInfos []pimpl.ExtensionInfo 106 107 // TypeRegistry is the registry to register each type descriptor. 108 // If nil, it uses protoregistry.GlobalTypes. 109 TypeRegistry interface { 110 RegisterMessage(protoreflect.MessageType) error 111 RegisterEnum(protoreflect.EnumType) error 112 RegisterExtension(protoreflect.ExtensionType) error 113 } 114} 115 116// Out is the output of the builder. 117type Out struct { 118 File protoreflect.FileDescriptor 119} 120 121func (tb Builder) Build() (out Out) { 122 // Replace the resolver with one that resolves dependencies by index, 123 // which is faster and more reliable than relying on the global registry. 124 if tb.File.FileRegistry == nil { 125 tb.File.FileRegistry = protoregistry.GlobalFiles 126 } 127 tb.File.FileRegistry = &resolverByIndex{ 128 goTypes: tb.GoTypes, 129 depIdxs: tb.DependencyIndexes, 130 fileRegistry: tb.File.FileRegistry, 131 } 132 133 // Initialize registry if unpopulated. 134 if tb.TypeRegistry == nil { 135 tb.TypeRegistry = protoregistry.GlobalTypes 136 } 137 138 fbOut := tb.File.Build() 139 out.File = fbOut.File 140 141 // Process enums. 142 enumGoTypes := tb.GoTypes[:len(fbOut.Enums)] 143 if len(tb.EnumInfos) != len(fbOut.Enums) { 144 panic("mismatching enum lengths") 145 } 146 if len(fbOut.Enums) > 0 { 147 for i := range fbOut.Enums { 148 tb.EnumInfos[i] = pimpl.EnumInfo{ 149 GoReflectType: reflect.TypeOf(enumGoTypes[i]), 150 Desc: &fbOut.Enums[i], 151 } 152 // Register enum types. 153 if err := tb.TypeRegistry.RegisterEnum(&tb.EnumInfos[i]); err != nil { 154 panic(err) 155 } 156 } 157 } 158 159 // Process messages. 160 messageGoTypes := tb.GoTypes[len(fbOut.Enums):][:len(fbOut.Messages)] 161 if len(tb.MessageInfos) != len(fbOut.Messages) { 162 panic("mismatching message lengths") 163 } 164 if len(fbOut.Messages) > 0 { 165 for i := range fbOut.Messages { 166 if messageGoTypes[i] == nil { 167 continue // skip map entry 168 } 169 170 tb.MessageInfos[i].GoReflectType = reflect.TypeOf(messageGoTypes[i]) 171 tb.MessageInfos[i].Desc = &fbOut.Messages[i] 172 173 // Register message types. 174 if err := tb.TypeRegistry.RegisterMessage(&tb.MessageInfos[i]); err != nil { 175 panic(err) 176 } 177 } 178 179 // As a special-case for descriptor.proto, 180 // locally register concrete message type for the options. 181 if out.File.Path() == "google/protobuf/descriptor.proto" && out.File.Package() == "google.protobuf" { 182 for i := range fbOut.Messages { 183 switch fbOut.Messages[i].Name() { 184 case "FileOptions": 185 descopts.File = messageGoTypes[i].(protoreflect.ProtoMessage) 186 case "EnumOptions": 187 descopts.Enum = messageGoTypes[i].(protoreflect.ProtoMessage) 188 case "EnumValueOptions": 189 descopts.EnumValue = messageGoTypes[i].(protoreflect.ProtoMessage) 190 case "MessageOptions": 191 descopts.Message = messageGoTypes[i].(protoreflect.ProtoMessage) 192 case "FieldOptions": 193 descopts.Field = messageGoTypes[i].(protoreflect.ProtoMessage) 194 case "OneofOptions": 195 descopts.Oneof = messageGoTypes[i].(protoreflect.ProtoMessage) 196 case "ExtensionRangeOptions": 197 descopts.ExtensionRange = messageGoTypes[i].(protoreflect.ProtoMessage) 198 case "ServiceOptions": 199 descopts.Service = messageGoTypes[i].(protoreflect.ProtoMessage) 200 case "MethodOptions": 201 descopts.Method = messageGoTypes[i].(protoreflect.ProtoMessage) 202 } 203 } 204 } 205 } 206 207 // Process extensions. 208 if len(tb.ExtensionInfos) != len(fbOut.Extensions) { 209 panic("mismatching extension lengths") 210 } 211 var depIdx int32 212 for i := range fbOut.Extensions { 213 // For enum and message kinds, determine the referent Go type so 214 // that we can construct their constructors. 215 const listExtDeps = 2 216 var goType reflect.Type 217 switch fbOut.Extensions[i].L1.Kind { 218 case protoreflect.EnumKind: 219 j := depIdxs.Get(tb.DependencyIndexes, listExtDeps, depIdx) 220 goType = reflect.TypeOf(tb.GoTypes[j]) 221 depIdx++ 222 case protoreflect.MessageKind, protoreflect.GroupKind: 223 j := depIdxs.Get(tb.DependencyIndexes, listExtDeps, depIdx) 224 goType = reflect.TypeOf(tb.GoTypes[j]) 225 depIdx++ 226 default: 227 goType = goTypeForPBKind[fbOut.Extensions[i].L1.Kind] 228 } 229 if fbOut.Extensions[i].IsList() { 230 goType = reflect.SliceOf(goType) 231 } 232 233 pimpl.InitExtensionInfo(&tb.ExtensionInfos[i], &fbOut.Extensions[i], goType) 234 235 // Register extension types. 236 if err := tb.TypeRegistry.RegisterExtension(&tb.ExtensionInfos[i]); err != nil { 237 panic(err) 238 } 239 } 240 241 return out 242} 243 244var goTypeForPBKind = map[protoreflect.Kind]reflect.Type{ 245 protoreflect.BoolKind: reflect.TypeOf(bool(false)), 246 protoreflect.Int32Kind: reflect.TypeOf(int32(0)), 247 protoreflect.Sint32Kind: reflect.TypeOf(int32(0)), 248 protoreflect.Sfixed32Kind: reflect.TypeOf(int32(0)), 249 protoreflect.Int64Kind: reflect.TypeOf(int64(0)), 250 protoreflect.Sint64Kind: reflect.TypeOf(int64(0)), 251 protoreflect.Sfixed64Kind: reflect.TypeOf(int64(0)), 252 protoreflect.Uint32Kind: reflect.TypeOf(uint32(0)), 253 protoreflect.Fixed32Kind: reflect.TypeOf(uint32(0)), 254 protoreflect.Uint64Kind: reflect.TypeOf(uint64(0)), 255 protoreflect.Fixed64Kind: reflect.TypeOf(uint64(0)), 256 protoreflect.FloatKind: reflect.TypeOf(float32(0)), 257 protoreflect.DoubleKind: reflect.TypeOf(float64(0)), 258 protoreflect.StringKind: reflect.TypeOf(string("")), 259 protoreflect.BytesKind: reflect.TypeOf([]byte(nil)), 260} 261 262type depIdxs []int32 263 264// Get retrieves the jth element of the ith sub-list. 265func (x depIdxs) Get(i, j int32) int32 { 266 return x[x[int32(len(x))-i-1]+j] 267} 268 269type ( 270 resolverByIndex struct { 271 goTypes []interface{} 272 depIdxs depIdxs 273 fileRegistry 274 } 275 fileRegistry interface { 276 FindFileByPath(string) (protoreflect.FileDescriptor, error) 277 FindDescriptorByName(protoreflect.FullName) (protoreflect.Descriptor, error) 278 RegisterFile(protoreflect.FileDescriptor) error 279 } 280) 281 282func (r *resolverByIndex) FindEnumByIndex(i, j int32, es []filedesc.Enum, ms []filedesc.Message) protoreflect.EnumDescriptor { 283 if depIdx := int(r.depIdxs.Get(i, j)); int(depIdx) < len(es)+len(ms) { 284 return &es[depIdx] 285 } else { 286 return pimpl.Export{}.EnumDescriptorOf(r.goTypes[depIdx]) 287 } 288} 289 290func (r *resolverByIndex) FindMessageByIndex(i, j int32, es []filedesc.Enum, ms []filedesc.Message) protoreflect.MessageDescriptor { 291 if depIdx := int(r.depIdxs.Get(i, j)); depIdx < len(es)+len(ms) { 292 return &ms[depIdx-len(es)] 293 } else { 294 return pimpl.Export{}.MessageDescriptorOf(r.goTypes[depIdx]) 295 } 296} 297