123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- // Copyright 2019 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- // Package filetype provides functionality for wrapping descriptors
- // with Go type information.
- package filetype
- import (
- "reflect"
- "google.golang.org/protobuf/internal/descopts"
- fdesc "google.golang.org/protobuf/internal/filedesc"
- pimpl "google.golang.org/protobuf/internal/impl"
- pref "google.golang.org/protobuf/reflect/protoreflect"
- preg "google.golang.org/protobuf/reflect/protoregistry"
- )
- // Builder constructs type descriptors from a raw file descriptor
- // and associated Go types for each enum and message declaration.
- //
- //
- // Flattened Ordering
- //
- // The protobuf type system represents declarations as a tree. Certain nodes in
- // the tree require us to either associate it with a concrete Go type or to
- // resolve a dependency, which is information that must be provided separately
- // since it cannot be derived from the file descriptor alone.
- //
- // However, representing a tree as Go literals is difficult to simply do in a
- // space and time efficient way. Thus, we store them as a flattened list of
- // objects where the serialization order from the tree-based form is important.
- //
- // The "flattened ordering" is defined as a tree traversal of all enum, message,
- // extension, and service declarations using the following algorithm:
- //
- // def VisitFileDecls(fd):
- // for e in fd.Enums: yield e
- // for m in fd.Messages: yield m
- // for x in fd.Extensions: yield x
- // for s in fd.Services: yield s
- // for m in fd.Messages: yield from VisitMessageDecls(m)
- //
- // def VisitMessageDecls(md):
- // for e in md.Enums: yield e
- // for m in md.Messages: yield m
- // for x in md.Extensions: yield x
- // for m in md.Messages: yield from VisitMessageDecls(m)
- //
- // The traversal starts at the root file descriptor and yields each direct
- // declaration within each node before traversing into sub-declarations
- // that children themselves may have.
- type Builder struct {
- // File is the underlying file descriptor builder.
- File fdesc.Builder
- // GoTypes is a unique set of the Go types for all declarations and
- // dependencies. Each type is represented as a zero value of the Go type.
- //
- // Declarations are Go types generated for enums and messages directly
- // declared (not publicly imported) in the proto source file.
- // Messages for map entries are accounted for, but represented by nil.
- // Enum declarations in "flattened ordering" come first, followed by
- // message declarations in "flattened ordering".
- //
- // Dependencies are Go types for enums or messages referenced by
- // message fields (excluding weak fields), for parent extended messages of
- // extension fields, for enums or messages referenced by extension fields,
- // and for input and output messages referenced by service methods.
- // Dependencies must come after declarations, but the ordering of
- // dependencies themselves is unspecified.
- GoTypes []interface{}
- // DependencyIndexes is an ordered list of indexes into GoTypes for the
- // dependencies of messages, extensions, or services.
- //
- // There are 5 sub-lists in "flattened ordering" concatenated back-to-back:
- // 0. Message field dependencies: list of the enum or message type
- // referred to by every message field.
- // 1. Extension field targets: list of the extended parent message of
- // every extension.
- // 2. Extension field dependencies: list of the enum or message type
- // referred to by every extension field.
- // 3. Service method inputs: list of the input message type
- // referred to by every service method.
- // 4. Service method outputs: list of the output message type
- // referred to by every service method.
- //
- // The offset into DependencyIndexes for the start of each sub-list
- // is appended to the end in reverse order.
- DependencyIndexes []int32
- // EnumInfos is a list of enum infos in "flattened ordering".
- EnumInfos []pimpl.EnumInfo
- // MessageInfos is a list of message infos in "flattened ordering".
- // If provided, the GoType and PBType for each element is populated.
- //
- // Requirement: len(MessageInfos) == len(Build.Messages)
- MessageInfos []pimpl.MessageInfo
- // ExtensionInfos is a list of extension infos in "flattened ordering".
- // Each element is initialized and registered with the protoregistry package.
- //
- // Requirement: len(LegacyExtensions) == len(Build.Extensions)
- ExtensionInfos []pimpl.ExtensionInfo
- // TypeRegistry is the registry to register each type descriptor.
- // If nil, it uses protoregistry.GlobalTypes.
- TypeRegistry interface {
- Register(...preg.Type) error
- }
- }
- // Out is the output of the builder.
- type Out struct {
- File pref.FileDescriptor
- }
- func (tb Builder) Build() (out Out) {
- // Replace the resolver with one that resolves dependencies by index,
- // which is faster and more reliable than relying on the global registry.
- if tb.File.FileRegistry == nil {
- tb.File.FileRegistry = preg.GlobalFiles
- }
- tb.File.FileRegistry = &resolverByIndex{
- goTypes: tb.GoTypes,
- depIdxs: tb.DependencyIndexes,
- fileRegistry: tb.File.FileRegistry,
- }
- // Initialize registry if unpopulated.
- if tb.TypeRegistry == nil {
- tb.TypeRegistry = preg.GlobalTypes
- }
- fbOut := tb.File.Build()
- out.File = fbOut.File
- // Process enums.
- enumGoTypes := tb.GoTypes[:len(fbOut.Enums)]
- if len(tb.EnumInfos) != len(fbOut.Enums) {
- panic("mismatching enum lengths")
- }
- if len(fbOut.Enums) > 0 {
- for i := range fbOut.Enums {
- tb.EnumInfos[i] = pimpl.EnumInfo{
- GoReflectType: reflect.TypeOf(enumGoTypes[i]),
- Desc: &fbOut.Enums[i],
- }
- // Register enum types.
- if err := tb.TypeRegistry.Register(&tb.EnumInfos[i]); err != nil {
- panic(err)
- }
- }
- }
- // Process messages.
- messageGoTypes := tb.GoTypes[len(fbOut.Enums):][:len(fbOut.Messages)]
- if len(tb.MessageInfos) != len(fbOut.Messages) {
- panic("mismatching message lengths")
- }
- if len(fbOut.Messages) > 0 {
- for i := range fbOut.Messages {
- if messageGoTypes[i] == nil {
- continue // skip map entry
- }
- tb.MessageInfos[i].GoReflectType = reflect.TypeOf(messageGoTypes[i])
- tb.MessageInfos[i].Desc = &fbOut.Messages[i]
- // Register message types.
- if err := tb.TypeRegistry.Register(&tb.MessageInfos[i]); err != nil {
- panic(err)
- }
- }
- // As a special-case for descriptor.proto,
- // locally register concrete message type for the options.
- if out.File.Path() == "google/protobuf/descriptor.proto" && out.File.Package() == "google.protobuf" {
- for i := range fbOut.Messages {
- switch fbOut.Messages[i].Name() {
- case "FileOptions":
- descopts.File = messageGoTypes[i].(pref.ProtoMessage)
- case "EnumOptions":
- descopts.Enum = messageGoTypes[i].(pref.ProtoMessage)
- case "EnumValueOptions":
- descopts.EnumValue = messageGoTypes[i].(pref.ProtoMessage)
- case "MessageOptions":
- descopts.Message = messageGoTypes[i].(pref.ProtoMessage)
- case "FieldOptions":
- descopts.Field = messageGoTypes[i].(pref.ProtoMessage)
- case "OneofOptions":
- descopts.Oneof = messageGoTypes[i].(pref.ProtoMessage)
- case "ExtensionRangeOptions":
- descopts.ExtensionRange = messageGoTypes[i].(pref.ProtoMessage)
- case "ServiceOptions":
- descopts.Service = messageGoTypes[i].(pref.ProtoMessage)
- case "MethodOptions":
- descopts.Method = messageGoTypes[i].(pref.ProtoMessage)
- }
- }
- }
- }
- // Process extensions.
- if len(tb.ExtensionInfos) != len(fbOut.Extensions) {
- panic("mismatching extension lengths")
- }
- var depIdx int32
- for i := range fbOut.Extensions {
- // For enum and message kinds, determine the referent Go type so
- // that we can construct their constructors.
- const listExtDeps = 2
- var goType reflect.Type
- switch fbOut.Extensions[i].L1.Kind {
- case pref.EnumKind:
- j := depIdxs.Get(tb.DependencyIndexes, listExtDeps, depIdx)
- goType = reflect.TypeOf(tb.GoTypes[j])
- depIdx++
- case pref.MessageKind, pref.GroupKind:
- j := depIdxs.Get(tb.DependencyIndexes, listExtDeps, depIdx)
- goType = reflect.TypeOf(tb.GoTypes[j])
- depIdx++
- default:
- goType = goTypeForPBKind[fbOut.Extensions[i].L1.Kind]
- }
- if fbOut.Extensions[i].IsList() {
- goType = reflect.SliceOf(goType)
- }
- pimpl.InitExtensionInfo(&tb.ExtensionInfos[i], &fbOut.Extensions[i], goType)
- // Register extension types.
- if err := tb.TypeRegistry.Register(&tb.ExtensionInfos[i]); err != nil {
- panic(err)
- }
- }
- return out
- }
- var goTypeForPBKind = map[pref.Kind]reflect.Type{
- pref.BoolKind: reflect.TypeOf(bool(false)),
- pref.Int32Kind: reflect.TypeOf(int32(0)),
- pref.Sint32Kind: reflect.TypeOf(int32(0)),
- pref.Sfixed32Kind: reflect.TypeOf(int32(0)),
- pref.Int64Kind: reflect.TypeOf(int64(0)),
- pref.Sint64Kind: reflect.TypeOf(int64(0)),
- pref.Sfixed64Kind: reflect.TypeOf(int64(0)),
- pref.Uint32Kind: reflect.TypeOf(uint32(0)),
- pref.Fixed32Kind: reflect.TypeOf(uint32(0)),
- pref.Uint64Kind: reflect.TypeOf(uint64(0)),
- pref.Fixed64Kind: reflect.TypeOf(uint64(0)),
- pref.FloatKind: reflect.TypeOf(float32(0)),
- pref.DoubleKind: reflect.TypeOf(float64(0)),
- pref.StringKind: reflect.TypeOf(string("")),
- pref.BytesKind: reflect.TypeOf([]byte(nil)),
- }
- type depIdxs []int32
- // Get retrieves the jth element of the ith sub-list.
- func (x depIdxs) Get(i, j int32) int32 {
- return x[x[int32(len(x))-i-1]+j]
- }
- type (
- resolverByIndex struct {
- goTypes []interface{}
- depIdxs depIdxs
- fileRegistry
- }
- fileRegistry interface {
- FindFileByPath(string) (pref.FileDescriptor, error)
- FindDescriptorByName(pref.FullName) (pref.Descriptor, error)
- Register(...pref.FileDescriptor) error
- }
- )
- func (r *resolverByIndex) FindEnumByIndex(i, j int32, es []fdesc.Enum, ms []fdesc.Message) pref.EnumDescriptor {
- if depIdx := int(r.depIdxs.Get(i, j)); int(depIdx) < len(es)+len(ms) {
- return &es[depIdx]
- } else {
- return pimpl.Export{}.EnumDescriptorOf(r.goTypes[depIdx])
- }
- }
- func (r *resolverByIndex) FindMessageByIndex(i, j int32, es []fdesc.Enum, ms []fdesc.Message) pref.MessageDescriptor {
- if depIdx := int(r.depIdxs.Get(i, j)); depIdx < len(es)+len(ms) {
- return &ms[depIdx-len(es)]
- } else {
- return pimpl.Export{}.MessageDescriptorOf(r.goTypes[depIdx])
- }
- }
|