build.go 10 KB

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