desc.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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 for converting descriptorpb.FileDescriptorProto
  5. // to/from the reflective protoreflect.FileDescriptor.
  6. package protodesc
  7. import (
  8. "google.golang.org/protobuf/internal/errors"
  9. "google.golang.org/protobuf/internal/filedesc"
  10. "google.golang.org/protobuf/internal/pragma"
  11. "google.golang.org/protobuf/internal/strs"
  12. "google.golang.org/protobuf/reflect/protoreflect"
  13. "google.golang.org/protobuf/reflect/protoregistry"
  14. "google.golang.org/protobuf/types/descriptorpb"
  15. )
  16. // Resolver is the resolver used by NewFile to resolve dependencies.
  17. // The enums and messages provided must belong to some parent file,
  18. // which is also registered.
  19. //
  20. // It is implemented by protoregistry.Files.
  21. type Resolver interface {
  22. FindFileByPath(string) (protoreflect.FileDescriptor, error)
  23. FindDescriptorByName(protoreflect.FullName) (protoreflect.Descriptor, error)
  24. }
  25. // option is an optional argument that may be provided to NewFile.
  26. type option struct {
  27. pragma.DoNotCompare
  28. allowUnresolvable bool
  29. }
  30. // allowUnresolvable configures NewFile to permissively allow unresolvable
  31. // file, enum, or message dependencies. Unresolved dependencies are replaced by
  32. // placeholder equivalents.
  33. //
  34. // The following dependencies may be left unresolved:
  35. // • Resolving an imported file.
  36. // • Resolving the type for a message field or extension field.
  37. // If the kind of the field is unknown, then a placeholder is used for both
  38. // protoreflect.FieldDescriptor.Enum and protoreflect.FieldDescriptor.Message.
  39. // • Resolving the enum value set as the default for an optional enum field.
  40. // If unresolvable, the protoreflect.FieldDescriptor.Default is set to the
  41. // first enum value in the associated enum (or zero if the also enum dependency
  42. // is also unresolvable). The protoreflect.FieldDescriptor.DefaultEnumValue
  43. // is set as a placeholder.
  44. // • Resolving the extended message type for an extension field.
  45. // • Resolving the input or output message type for a service method.
  46. //
  47. // If the unresolved dependency uses a relative name, then the placeholder will
  48. // contain an invalid FullName with a "*." prefix, indicating that the starting
  49. // prefix of the full name is unknown.
  50. func allowUnresolvable() option {
  51. return option{allowUnresolvable: true}
  52. }
  53. // NewFile creates a new protoreflect.FileDescriptor from the provided
  54. // file descriptor message. The file must represent a valid proto file according
  55. // to protobuf semantics. The returned descriptor is a deep copy of the input.
  56. //
  57. // Any imported files, enum types, or message types referenced in the file are
  58. // resolved using the provided registry. When looking up an import file path,
  59. // the path must be unique. The newly created file descriptor is not registered
  60. // back into the provided file registry.
  61. func NewFile(fd *descriptorpb.FileDescriptorProto, r Resolver) (protoreflect.FileDescriptor, error) {
  62. // TODO: remove setting allowUnresolvable once naughty users are migrated.
  63. return newFile(fd, r, allowUnresolvable())
  64. }
  65. func newFile(fd *descriptorpb.FileDescriptorProto, r Resolver, opts ...option) (protoreflect.FileDescriptor, error) {
  66. // Process the options.
  67. var allowUnresolvable bool
  68. for _, o := range opts {
  69. allowUnresolvable = allowUnresolvable || o.allowUnresolvable
  70. }
  71. if r == nil {
  72. r = (*protoregistry.Files)(nil) // empty resolver
  73. }
  74. // Handle the file descriptor content.
  75. f := &filedesc.File{L2: &filedesc.FileL2{}}
  76. switch fd.GetSyntax() {
  77. case "proto2", "":
  78. f.L1.Syntax = protoreflect.Proto2
  79. case "proto3":
  80. f.L1.Syntax = protoreflect.Proto3
  81. default:
  82. return nil, errors.New("invalid syntax: %q", fd.GetSyntax())
  83. }
  84. f.L1.Path = fd.GetName()
  85. if f.L1.Path == "" {
  86. return nil, errors.New("file path must be populated")
  87. }
  88. f.L1.Package = protoreflect.FullName(fd.GetPackage())
  89. if !f.L1.Package.IsValid() && f.L1.Package != "" {
  90. return nil, errors.New("invalid package: %q", f.L1.Package)
  91. }
  92. if opts := fd.GetOptions(); opts != nil {
  93. opts = clone(opts).(*descriptorpb.FileOptions)
  94. f.L2.Options = func() protoreflect.ProtoMessage { return opts }
  95. }
  96. f.L2.Imports = make(filedesc.FileImports, len(fd.GetDependency()))
  97. for _, i := range fd.GetPublicDependency() {
  98. if !(0 <= i && int(i) < len(f.L2.Imports)) || f.L2.Imports[i].IsPublic {
  99. return nil, errors.New("invalid or duplicate public import index: %d", i)
  100. }
  101. f.L2.Imports[i].IsPublic = true
  102. }
  103. for _, i := range fd.GetWeakDependency() {
  104. if !(0 <= i && int(i) < len(f.L2.Imports)) || f.L2.Imports[i].IsWeak {
  105. return nil, errors.New("invalid or duplicate weak import index: %d", i)
  106. }
  107. f.L2.Imports[i].IsWeak = true
  108. }
  109. imps := importSet{f.Path(): true}
  110. for i, path := range fd.GetDependency() {
  111. imp := &f.L2.Imports[i]
  112. f, err := r.FindFileByPath(path)
  113. if err == protoregistry.NotFound && (allowUnresolvable || imp.IsWeak) {
  114. f = filedesc.PlaceholderFile(path)
  115. } else if err != nil {
  116. return nil, errors.New("could not resolve import %q: %v", path, err)
  117. }
  118. imp.FileDescriptor = f
  119. if imps[imp.Path()] {
  120. return nil, errors.New("already imported %q", path)
  121. }
  122. imps[imp.Path()] = true
  123. }
  124. for i := range fd.GetDependency() {
  125. imp := &f.L2.Imports[i]
  126. imps.importPublic(imp.Imports())
  127. }
  128. // Handle source locations.
  129. for _, loc := range fd.GetSourceCodeInfo().GetLocation() {
  130. var l protoreflect.SourceLocation
  131. // TODO: Validate that the path points to an actual declaration?
  132. l.Path = protoreflect.SourcePath(loc.GetPath())
  133. s := loc.GetSpan()
  134. switch len(s) {
  135. case 3:
  136. l.StartLine, l.StartColumn, l.EndLine, l.EndColumn = int(s[0]), int(s[1]), int(s[0]), int(s[2])
  137. case 4:
  138. l.StartLine, l.StartColumn, l.EndLine, l.EndColumn = int(s[0]), int(s[1]), int(s[2]), int(s[3])
  139. default:
  140. return nil, errors.New("invalid span: %v", s)
  141. }
  142. // TODO: Validate that the span information is sensible?
  143. // See https://github.com/protocolbuffers/protobuf/issues/6378.
  144. if false && (l.EndLine < l.StartLine || l.StartLine < 0 || l.StartColumn < 0 || l.EndColumn < 0 ||
  145. (l.StartLine == l.EndLine && l.EndColumn <= l.StartColumn)) {
  146. return nil, errors.New("invalid span: %v", s)
  147. }
  148. l.LeadingDetachedComments = loc.GetLeadingDetachedComments()
  149. l.LeadingComments = loc.GetLeadingComments()
  150. l.TrailingComments = loc.GetTrailingComments()
  151. f.L2.Locations.List = append(f.L2.Locations.List, l)
  152. }
  153. // Step 1: Allocate and derive the names for all declarations.
  154. // This copies all fields from the descriptor proto except:
  155. // google.protobuf.FieldDescriptorProto.type_name
  156. // google.protobuf.FieldDescriptorProto.default_value
  157. // google.protobuf.FieldDescriptorProto.oneof_index
  158. // google.protobuf.FieldDescriptorProto.extendee
  159. // google.protobuf.MethodDescriptorProto.input
  160. // google.protobuf.MethodDescriptorProto.output
  161. var err error
  162. sb := new(strs.Builder)
  163. r1 := make(descsByName)
  164. if f.L1.Enums.List, err = r1.initEnumDeclarations(fd.GetEnumType(), f, sb); err != nil {
  165. return nil, err
  166. }
  167. if f.L1.Messages.List, err = r1.initMessagesDeclarations(fd.GetMessageType(), f, sb); err != nil {
  168. return nil, err
  169. }
  170. if f.L1.Extensions.List, err = r1.initExtensionDeclarations(fd.GetExtension(), f, sb); err != nil {
  171. return nil, err
  172. }
  173. if f.L1.Services.List, err = r1.initServiceDeclarations(fd.GetService(), f, sb); err != nil {
  174. return nil, err
  175. }
  176. // Step 2: Resolve every dependency reference not handled by step 1.
  177. r2 := &resolver{local: r1, remote: r, imports: imps, allowUnresolvable: allowUnresolvable}
  178. if err := r2.resolveMessageDependencies(f.L1.Messages.List, fd.GetMessageType()); err != nil {
  179. return nil, err
  180. }
  181. if err := r2.resolveExtensionDependencies(f.L1.Extensions.List, fd.GetExtension()); err != nil {
  182. return nil, err
  183. }
  184. if err := r2.resolveServiceDependencies(f.L1.Services.List, fd.GetService()); err != nil {
  185. return nil, err
  186. }
  187. // Step 3: Validate every enum, message, and extension declaration.
  188. if err := validateEnumDeclarations(f.L1.Enums.List, fd.GetEnumType()); err != nil {
  189. return nil, err
  190. }
  191. if err := validateMessageDeclarations(f.L1.Messages.List, fd.GetMessageType()); err != nil {
  192. return nil, err
  193. }
  194. if err := validateExtensionDeclarations(f.L1.Extensions.List, fd.GetExtension()); err != nil {
  195. return nil, err
  196. }
  197. return f, nil
  198. }
  199. type importSet map[string]bool
  200. func (is importSet) importPublic(imps protoreflect.FileImports) {
  201. for i := 0; i < imps.Len(); i++ {
  202. if imp := imps.Get(i); imp.IsPublic {
  203. is[imp.Path()] = true
  204. is.importPublic(imp.Imports())
  205. }
  206. }
  207. }