desc.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. // 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. // Package protodesc provides functionality for converting
  5. // FileDescriptorProto messages to/from [protoreflect.FileDescriptor] values.
  6. //
  7. // The google.protobuf.FileDescriptorProto is a protobuf message that describes
  8. // the type information for a .proto file in a form that is easily serializable.
  9. // The [protoreflect.FileDescriptor] is a more structured representation of
  10. // the FileDescriptorProto message where references and remote dependencies
  11. // can be directly followed.
  12. package protodesc
  13. import (
  14. "google.golang.org/protobuf/internal/editionssupport"
  15. "google.golang.org/protobuf/internal/errors"
  16. "google.golang.org/protobuf/internal/filedesc"
  17. "google.golang.org/protobuf/internal/pragma"
  18. "google.golang.org/protobuf/internal/strs"
  19. "google.golang.org/protobuf/proto"
  20. "google.golang.org/protobuf/reflect/protoreflect"
  21. "google.golang.org/protobuf/reflect/protoregistry"
  22. "google.golang.org/protobuf/types/descriptorpb"
  23. )
  24. // Resolver is the resolver used by [NewFile] to resolve dependencies.
  25. // The enums and messages provided must belong to some parent file,
  26. // which is also registered.
  27. //
  28. // It is implemented by [protoregistry.Files].
  29. type Resolver interface {
  30. FindFileByPath(string) (protoreflect.FileDescriptor, error)
  31. FindDescriptorByName(protoreflect.FullName) (protoreflect.Descriptor, error)
  32. }
  33. // FileOptions configures the construction of file descriptors.
  34. type FileOptions struct {
  35. pragma.NoUnkeyedLiterals
  36. // AllowUnresolvable configures New to permissively allow unresolvable
  37. // file, enum, or message dependencies. Unresolved dependencies are replaced
  38. // by placeholder equivalents.
  39. //
  40. // The following dependencies may be left unresolved:
  41. // • Resolving an imported file.
  42. // • Resolving the type for a message field or extension field.
  43. // If the kind of the field is unknown, then a placeholder is used for both
  44. // the Enum and Message accessors on the protoreflect.FieldDescriptor.
  45. // • Resolving an enum value set as the default for an optional enum field.
  46. // If unresolvable, the protoreflect.FieldDescriptor.Default is set to the
  47. // first value in the associated enum (or zero if the also enum dependency
  48. // is also unresolvable). The protoreflect.FieldDescriptor.DefaultEnumValue
  49. // is populated with a placeholder.
  50. // • Resolving the extended message type for an extension field.
  51. // • Resolving the input or output message type for a service method.
  52. //
  53. // If the unresolved dependency uses a relative name,
  54. // then the placeholder will contain an invalid FullName with a "*." prefix,
  55. // indicating that the starting prefix of the full name is unknown.
  56. AllowUnresolvable bool
  57. }
  58. // NewFile creates a new [protoreflect.FileDescriptor] from the provided
  59. // file descriptor message. See [FileOptions.New] for more information.
  60. func NewFile(fd *descriptorpb.FileDescriptorProto, r Resolver) (protoreflect.FileDescriptor, error) {
  61. return FileOptions{}.New(fd, r)
  62. }
  63. // NewFiles creates a new [protoregistry.Files] from the provided
  64. // FileDescriptorSet message. See [FileOptions.NewFiles] for more information.
  65. func NewFiles(fd *descriptorpb.FileDescriptorSet) (*protoregistry.Files, error) {
  66. return FileOptions{}.NewFiles(fd)
  67. }
  68. // New creates a new [protoreflect.FileDescriptor] from the provided
  69. // file descriptor message. The file must represent a valid proto file according
  70. // to protobuf semantics. The returned descriptor is a deep copy of the input.
  71. //
  72. // Any imported files, enum types, or message types referenced in the file are
  73. // resolved using the provided registry. When looking up an import file path,
  74. // the path must be unique. The newly created file descriptor is not registered
  75. // back into the provided file registry.
  76. func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (protoreflect.FileDescriptor, error) {
  77. if r == nil {
  78. r = (*protoregistry.Files)(nil) // empty resolver
  79. }
  80. // Handle the file descriptor content.
  81. f := &filedesc.File{L2: &filedesc.FileL2{}}
  82. switch fd.GetSyntax() {
  83. case "proto2", "":
  84. f.L1.Syntax = protoreflect.Proto2
  85. f.L1.Edition = filedesc.EditionProto2
  86. case "proto3":
  87. f.L1.Syntax = protoreflect.Proto3
  88. f.L1.Edition = filedesc.EditionProto3
  89. case "editions":
  90. f.L1.Syntax = protoreflect.Editions
  91. f.L1.Edition = fromEditionProto(fd.GetEdition())
  92. default:
  93. return nil, errors.New("invalid syntax: %q", fd.GetSyntax())
  94. }
  95. if f.L1.Syntax == protoreflect.Editions && (fd.GetEdition() < editionssupport.Minimum || fd.GetEdition() > editionssupport.Maximum) {
  96. return nil, errors.New("use of edition %v not yet supported by the Go Protobuf runtime", fd.GetEdition())
  97. }
  98. f.L1.Path = fd.GetName()
  99. if f.L1.Path == "" {
  100. return nil, errors.New("file path must be populated")
  101. }
  102. f.L1.Package = protoreflect.FullName(fd.GetPackage())
  103. if !f.L1.Package.IsValid() && f.L1.Package != "" {
  104. return nil, errors.New("invalid package: %q", f.L1.Package)
  105. }
  106. if opts := fd.GetOptions(); opts != nil {
  107. opts = proto.Clone(opts).(*descriptorpb.FileOptions)
  108. f.L2.Options = func() protoreflect.ProtoMessage { return opts }
  109. }
  110. initFileDescFromFeatureSet(f, fd.GetOptions().GetFeatures())
  111. f.L2.Imports = make(filedesc.FileImports, len(fd.GetDependency()))
  112. for _, i := range fd.GetPublicDependency() {
  113. if !(0 <= i && int(i) < len(f.L2.Imports)) || f.L2.Imports[i].IsPublic {
  114. return nil, errors.New("invalid or duplicate public import index: %d", i)
  115. }
  116. f.L2.Imports[i].IsPublic = true
  117. }
  118. for _, i := range fd.GetWeakDependency() {
  119. if !(0 <= i && int(i) < len(f.L2.Imports)) || f.L2.Imports[i].IsWeak {
  120. return nil, errors.New("invalid or duplicate weak import index: %d", i)
  121. }
  122. f.L2.Imports[i].IsWeak = true
  123. }
  124. imps := importSet{f.Path(): true}
  125. for i, path := range fd.GetDependency() {
  126. imp := &f.L2.Imports[i]
  127. f, err := r.FindFileByPath(path)
  128. if err == protoregistry.NotFound && (o.AllowUnresolvable || imp.IsWeak) {
  129. f = filedesc.PlaceholderFile(path)
  130. } else if err != nil {
  131. return nil, errors.New("could not resolve import %q: %v", path, err)
  132. }
  133. imp.FileDescriptor = f
  134. if imps[imp.Path()] {
  135. return nil, errors.New("already imported %q", path)
  136. }
  137. imps[imp.Path()] = true
  138. }
  139. for i := range fd.GetDependency() {
  140. imp := &f.L2.Imports[i]
  141. imps.importPublic(imp.Imports())
  142. }
  143. // Handle source locations.
  144. f.L2.Locations.File = f
  145. for _, loc := range fd.GetSourceCodeInfo().GetLocation() {
  146. var l protoreflect.SourceLocation
  147. // TODO: Validate that the path points to an actual declaration?
  148. l.Path = protoreflect.SourcePath(loc.GetPath())
  149. s := loc.GetSpan()
  150. switch len(s) {
  151. case 3:
  152. l.StartLine, l.StartColumn, l.EndLine, l.EndColumn = int(s[0]), int(s[1]), int(s[0]), int(s[2])
  153. case 4:
  154. l.StartLine, l.StartColumn, l.EndLine, l.EndColumn = int(s[0]), int(s[1]), int(s[2]), int(s[3])
  155. default:
  156. return nil, errors.New("invalid span: %v", s)
  157. }
  158. // TODO: Validate that the span information is sensible?
  159. // See https://github.com/protocolbuffers/protobuf/issues/6378.
  160. if false && (l.EndLine < l.StartLine || l.StartLine < 0 || l.StartColumn < 0 || l.EndColumn < 0 ||
  161. (l.StartLine == l.EndLine && l.EndColumn <= l.StartColumn)) {
  162. return nil, errors.New("invalid span: %v", s)
  163. }
  164. l.LeadingDetachedComments = loc.GetLeadingDetachedComments()
  165. l.LeadingComments = loc.GetLeadingComments()
  166. l.TrailingComments = loc.GetTrailingComments()
  167. f.L2.Locations.List = append(f.L2.Locations.List, l)
  168. }
  169. // Step 1: Allocate and derive the names for all declarations.
  170. // This copies all fields from the descriptor proto except:
  171. // google.protobuf.FieldDescriptorProto.type_name
  172. // google.protobuf.FieldDescriptorProto.default_value
  173. // google.protobuf.FieldDescriptorProto.oneof_index
  174. // google.protobuf.FieldDescriptorProto.extendee
  175. // google.protobuf.MethodDescriptorProto.input
  176. // google.protobuf.MethodDescriptorProto.output
  177. var err error
  178. sb := new(strs.Builder)
  179. r1 := make(descsByName)
  180. if f.L1.Enums.List, err = r1.initEnumDeclarations(fd.GetEnumType(), f, sb); err != nil {
  181. return nil, err
  182. }
  183. if f.L1.Messages.List, err = r1.initMessagesDeclarations(fd.GetMessageType(), f, sb); err != nil {
  184. return nil, err
  185. }
  186. if f.L1.Extensions.List, err = r1.initExtensionDeclarations(fd.GetExtension(), f, sb); err != nil {
  187. return nil, err
  188. }
  189. if f.L1.Services.List, err = r1.initServiceDeclarations(fd.GetService(), f, sb); err != nil {
  190. return nil, err
  191. }
  192. // Step 2: Resolve every dependency reference not handled by step 1.
  193. r2 := &resolver{local: r1, remote: r, imports: imps, allowUnresolvable: o.AllowUnresolvable}
  194. if err := r2.resolveMessageDependencies(f.L1.Messages.List, fd.GetMessageType()); err != nil {
  195. return nil, err
  196. }
  197. if err := r2.resolveExtensionDependencies(f.L1.Extensions.List, fd.GetExtension()); err != nil {
  198. return nil, err
  199. }
  200. if err := r2.resolveServiceDependencies(f.L1.Services.List, fd.GetService()); err != nil {
  201. return nil, err
  202. }
  203. // Step 3: Validate every enum, message, and extension declaration.
  204. if err := validateEnumDeclarations(f.L1.Enums.List, fd.GetEnumType()); err != nil {
  205. return nil, err
  206. }
  207. if err := validateMessageDeclarations(f, f.L1.Messages.List, fd.GetMessageType()); err != nil {
  208. return nil, err
  209. }
  210. if err := validateExtensionDeclarations(f, f.L1.Extensions.List, fd.GetExtension()); err != nil {
  211. return nil, err
  212. }
  213. return f, nil
  214. }
  215. type importSet map[string]bool
  216. func (is importSet) importPublic(imps protoreflect.FileImports) {
  217. for i := 0; i < imps.Len(); i++ {
  218. if imp := imps.Get(i); imp.IsPublic {
  219. is[imp.Path()] = true
  220. is.importPublic(imp.Imports())
  221. }
  222. }
  223. }
  224. // NewFiles creates a new [protoregistry.Files] from the provided
  225. // FileDescriptorSet message. The descriptor set must include only
  226. // valid files according to protobuf semantics. The returned descriptors
  227. // are a deep copy of the input.
  228. func (o FileOptions) NewFiles(fds *descriptorpb.FileDescriptorSet) (*protoregistry.Files, error) {
  229. files := make(map[string]*descriptorpb.FileDescriptorProto)
  230. for _, fd := range fds.File {
  231. if _, ok := files[fd.GetName()]; ok {
  232. return nil, errors.New("file appears multiple times: %q", fd.GetName())
  233. }
  234. files[fd.GetName()] = fd
  235. }
  236. r := &protoregistry.Files{}
  237. for _, fd := range files {
  238. if err := o.addFileDeps(r, fd, files); err != nil {
  239. return nil, err
  240. }
  241. }
  242. return r, nil
  243. }
  244. func (o FileOptions) addFileDeps(r *protoregistry.Files, fd *descriptorpb.FileDescriptorProto, files map[string]*descriptorpb.FileDescriptorProto) error {
  245. // Set the entry to nil while descending into a file's dependencies to detect cycles.
  246. files[fd.GetName()] = nil
  247. for _, dep := range fd.Dependency {
  248. depfd, ok := files[dep]
  249. if depfd == nil {
  250. if ok {
  251. return errors.New("import cycle in file: %q", dep)
  252. }
  253. continue
  254. }
  255. if err := o.addFileDeps(r, depfd, files); err != nil {
  256. return err
  257. }
  258. }
  259. // Delete the entry once dependencies are processed.
  260. delete(files, fd.GetName())
  261. f, err := o.New(fd, r)
  262. if err != nil {
  263. return err
  264. }
  265. return r.RegisterFile(f)
  266. }