Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

297 rader
10 KiB

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