registry.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  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 proto
  5. import (
  6. "bytes"
  7. "compress/gzip"
  8. "fmt"
  9. "io/ioutil"
  10. "os"
  11. "reflect"
  12. "runtime"
  13. "strings"
  14. "sync"
  15. protoV2 "google.golang.org/protobuf/proto"
  16. "google.golang.org/protobuf/reflect/protodesc"
  17. pref "google.golang.org/protobuf/reflect/protoreflect"
  18. "google.golang.org/protobuf/reflect/protoregistry"
  19. "google.golang.org/protobuf/runtime/protoimpl"
  20. "google.golang.org/protobuf/types/descriptorpb"
  21. )
  22. // filePath is the path to the proto source file.
  23. type filePath = string // e.g., "google/protobuf/descriptor.proto"
  24. // fileDescGZIP is the compressed contents of the encoded FileDescriptorProto.
  25. type fileDescGZIP = []byte
  26. var fileCache sync.Map // map[filePath]fileDescGZIP
  27. // RegisterFile is called from generated code and registers the compressed
  28. // FileDescriptorProto with the file path for a proto source file.
  29. //
  30. // Deprecated: Use protoregistry.GlobalFiles.Register instead.
  31. func RegisterFile(s filePath, d fileDescGZIP) {
  32. // Decompress the descriptor.
  33. zr, err := gzip.NewReader(bytes.NewReader(d))
  34. if err != nil {
  35. panic(fmt.Sprintf("proto: invalid compressed file descriptor: %v", err))
  36. }
  37. b, err := ioutil.ReadAll(zr)
  38. if err != nil {
  39. panic(fmt.Sprintf("proto: invalid compressed file descriptor: %v", err))
  40. }
  41. // Parse the raw descriptor proto.
  42. var pb descriptorpb.FileDescriptorProto
  43. if err := protoV2.Unmarshal(b, &pb); err != nil {
  44. panic(fmt.Sprintf("proto: unmarshal failure: %v", err))
  45. }
  46. // Some old descriptors don't include the filename.
  47. if pb.Name == nil {
  48. pb.Name = &s
  49. }
  50. // Convert the raw descriptor to a structured file descriptor.
  51. fd, err := protodesc.NewFile(&pb, nil)
  52. if err != nil {
  53. // TODO: Ignore errors due to placeholders.
  54. panic(fmt.Sprintf("proto: descriptor parsing failure: %v", err))
  55. }
  56. // Register the descriptor in the v2 registry and cache the result locally.
  57. if err := protoregistry.GlobalFiles.Register(fd); err != nil {
  58. printWarning(err)
  59. return
  60. }
  61. fileCache.Store(s, b)
  62. }
  63. // FileDescriptor returns the compressed FileDescriptorProto given the file path
  64. // for a proto source file. It returns nil if not found.
  65. //
  66. // Deprecated: Use protoregistry.GlobalFiles.RangeFilesByPath instead.
  67. func FileDescriptor(s filePath) (d fileDescGZIP) {
  68. if d, ok := fileCache.Load(s); ok {
  69. return d.(fileDescGZIP)
  70. }
  71. // Find the descriptor in the v2 registry.
  72. if fd, _ := protoregistry.GlobalFiles.FindFileByPath(s); fd != nil {
  73. pb := protodesc.ToFileDescriptorProto(fd)
  74. b, err := protoV2.Marshal(pb)
  75. if err != nil {
  76. panic(fmt.Sprintf("proto: marshal failure: %v", err))
  77. }
  78. bb := new(bytes.Buffer)
  79. zw := gzip.NewWriter(bb)
  80. if _, err := zw.Write(b); err != nil {
  81. panic(fmt.Sprintf("proto: compression failure: %v", err))
  82. }
  83. if err := zw.Close(); err != nil {
  84. panic(fmt.Sprintf("proto: compression failure: %v", err))
  85. }
  86. d = bb.Bytes()
  87. }
  88. // Locally cache the raw descriptor form for the file.
  89. if len(d) > 0 {
  90. fileCache.Store(s, d)
  91. }
  92. return d
  93. }
  94. // enumName is the name of an enum. For historical reasons, the enum name is
  95. // neither the full Go name nor the full protobuf name of the enum.
  96. // The name is the dot-separated combination of just the proto package that the
  97. // enum is declared within followed by the Go type name of the generated enum.
  98. type enumName = string // e.g., "my.proto.package.GoMessage_GoEnum"
  99. // enumsByName maps enum values by name to their numeric counterpart.
  100. type enumsByName = map[string]int32
  101. // enumsByNumber maps enum values by number to their name counterpart.
  102. type enumsByNumber = map[int32]string
  103. var enumCache sync.Map // map[enumName]enumsByName
  104. // RegisterEnum is called from the generated code and registers the mapping of
  105. // enum value names to enum numbers for the enum identified by s.
  106. //
  107. // Deprecated: Use protoregistry.GlobalTypes.Register instead.
  108. func RegisterEnum(s enumName, _ enumsByNumber, m enumsByName) {
  109. if _, ok := enumCache.Load(s); ok {
  110. panic("proto: duplicate enum registered: " + s)
  111. }
  112. enumCache.Store(s, m)
  113. // This does not forward registration to the v2 registry since this API
  114. // lacks sufficient information to construct a complete v2 enum descriptor.
  115. }
  116. // EnumValueMap returns the mapping from enum value names to enum numbers for
  117. // the enum of the given name. It returns nil if not found.
  118. //
  119. // Deprecated: Use protoregistry.GlobalTypes.FindEnumByName instead.
  120. func EnumValueMap(s enumName) (m enumsByName) {
  121. v, ok := enumCache.Load(s)
  122. if ok {
  123. return v.(enumsByName)
  124. }
  125. // Construct the mapping from a v2 enum descriptor.
  126. var protoPkg pref.FullName
  127. if i := strings.LastIndexByte(s, '.'); i >= 0 {
  128. protoPkg = pref.FullName(s[:i])
  129. }
  130. protoregistry.GlobalFiles.RangeFilesByPackage(pref.FullName(protoPkg), func(fd pref.FileDescriptor) bool {
  131. return walkEnums(fd, func(ed pref.EnumDescriptor) bool {
  132. if s == hybridEnumName(ed) {
  133. m = make(enumsByName)
  134. evs := ed.Values()
  135. for i := evs.Len() - 1; i >= 0; i-- {
  136. ev := evs.Get(i)
  137. m[string(ev.Name())] = int32(ev.Number())
  138. }
  139. return false
  140. }
  141. return true
  142. })
  143. })
  144. if m != nil {
  145. enumCache.Store(s, m)
  146. }
  147. return m
  148. }
  149. // walkEnums recursively walks all enums declared in d.
  150. func walkEnums(d interface {
  151. Enums() pref.EnumDescriptors
  152. Messages() pref.MessageDescriptors
  153. }, f func(pref.EnumDescriptor) bool) bool {
  154. cont := true
  155. eds := d.Enums()
  156. for i := eds.Len() - 1; cont && i >= 0; i-- {
  157. cont = cont && f(eds.Get(i))
  158. }
  159. mds := d.Messages()
  160. for i := mds.Len() - 1; cont && i >= 0; i-- {
  161. cont = cont && walkEnums(mds.Get(i), f)
  162. }
  163. return cont
  164. }
  165. // hybridEnumName returns the legacy enum identifier.
  166. func hybridEnumName(ed pref.EnumDescriptor) enumName {
  167. var protoPkg string
  168. if fd := ed.ParentFile(); fd != nil {
  169. protoPkg = string(fd.Package())
  170. }
  171. if protoPkg == "" {
  172. return camelCase(string(ed.FullName()))
  173. }
  174. return protoPkg + "." + camelCase(strings.TrimPrefix(string(ed.FullName()), protoPkg+"."))
  175. }
  176. // camelCase is a copy of the v2 protogen.camelCase function.
  177. func camelCase(s string) string {
  178. isASCIILower := func(c byte) bool {
  179. return 'a' <= c && c <= 'z'
  180. }
  181. isASCIIDigit := func(c byte) bool {
  182. return '0' <= c && c <= '9'
  183. }
  184. var b []byte
  185. for i := 0; i < len(s); i++ {
  186. c := s[i]
  187. switch {
  188. case c == '.' && i+1 < len(s) && isASCIILower(s[i+1]):
  189. continue
  190. case c == '.':
  191. b = append(b, '_')
  192. case c == '_' && (i == 0 || s[i-1] == '.'):
  193. b = append(b, 'X')
  194. case c == '_' && i+1 < len(s) && isASCIILower(s[i+1]):
  195. continue
  196. case isASCIIDigit(c):
  197. b = append(b, c)
  198. default:
  199. if isASCIILower(c) {
  200. c -= 'a' - 'A'
  201. }
  202. b = append(b, c)
  203. for ; i+1 < len(s) && isASCIILower(s[i+1]); i++ {
  204. b = append(b, s[i+1])
  205. }
  206. }
  207. }
  208. return string(b)
  209. }
  210. // messageName is the full name of protobuf message.
  211. type messageName = string
  212. var messageTypeCache sync.Map // map[messageName]reflect.Type
  213. // RegisterType is called from generated code and register the message Go type
  214. // for a message of the given name.
  215. //
  216. // Deprecated: Use protoregistry.GlobalTypes.Register instead.
  217. func RegisterType(m Message, s messageName) {
  218. mt := protoimpl.X.MessageTypeOf(m)
  219. if s != messageName(mt.Descriptor().FullName()) {
  220. panic(fmt.Sprintf("proto: inconsistent message name: got %v, want %v", s, mt.Descriptor().FullName()))
  221. }
  222. if err := protoregistry.GlobalTypes.Register(mt); err != nil {
  223. printWarning(err)
  224. return
  225. }
  226. messageTypeCache.Store(s, reflect.TypeOf(m))
  227. }
  228. // RegisterMapType is called from generated code and registers the Go map type
  229. // for a protobuf message representing a map entry.
  230. //
  231. // Deprecated: Do not use.
  232. func RegisterMapType(m interface{}, s messageName) {
  233. t := reflect.TypeOf(m)
  234. if t.Kind() != reflect.Map {
  235. panic(fmt.Sprintf("invalid map kind: %v", t))
  236. }
  237. if _, ok := messageTypeCache.Load(s); ok {
  238. printWarning(fmt.Errorf("proto: duplicate proto message registered: %s", s))
  239. return
  240. }
  241. messageTypeCache.Store(s, t)
  242. }
  243. // MessageType returns the message type for a named message.
  244. // It returns nil if not found.
  245. //
  246. // Deprecated: Use protoregistry.GlobalTypes.FindMessageByName instead.
  247. func MessageType(s messageName) reflect.Type {
  248. if t, ok := messageTypeCache.Load(s); ok {
  249. return t.(reflect.Type)
  250. }
  251. // Derive the message type from the v2 registry.
  252. var t reflect.Type
  253. mt, _ := protoregistry.GlobalTypes.FindMessageByName(pref.FullName(s))
  254. if mt != nil {
  255. t = reflect.TypeOf(mt.Zero().Interface())
  256. }
  257. // TODO: Support retrieving Go map types for map entry messages?
  258. if t != nil {
  259. messageTypeCache.Store(s, t)
  260. }
  261. return t
  262. }
  263. // MessageName returns the full protobuf name for the given message type.
  264. //
  265. // Deprecated: Use pref.MessageDescriptor.FullName instead.
  266. func MessageName(m Message) messageName {
  267. if m, ok := m.(interface {
  268. XXX_MessageName() messageName
  269. }); ok {
  270. return m.XXX_MessageName()
  271. }
  272. return messageName(protoimpl.X.MessageDescriptorOf(m).FullName())
  273. }
  274. // RegisterExtension is called from the generated code and registers
  275. // the extension descriptor.
  276. //
  277. // Deprecated: Use protoregistry.GlobalTypes.Register instead.
  278. func RegisterExtension(d *ExtensionDesc) {
  279. if err := protoregistry.GlobalTypes.Register(d); err != nil {
  280. panic(err)
  281. }
  282. }
  283. type extensionsByNumber = map[int32]*ExtensionDesc
  284. var extensionCache sync.Map // map[messageName]extensionsByNumber
  285. // RegisteredExtensions returns a map of the registered extensions for the
  286. // provided protobuf message, indexed by the extension field number.
  287. //
  288. // Deprecated: Use protoregistry.GlobalTypes.RangeExtensionsByMessage instead.
  289. func RegisteredExtensions(m Message) extensionsByNumber {
  290. s := MessageName(m)
  291. if xs, ok := extensionCache.Load(s); ok {
  292. return xs.(extensionsByNumber)
  293. }
  294. var xs extensionsByNumber
  295. protoregistry.GlobalTypes.RangeExtensionsByMessage(pref.FullName(s), func(xt pref.ExtensionType) bool {
  296. if xs == nil {
  297. xs = make(extensionsByNumber)
  298. }
  299. xs[int32(xt.TypeDescriptor().Number())] = protoimpl.X.ExtensionDescFromType(xt)
  300. return true
  301. })
  302. if xs == nil {
  303. return nil
  304. }
  305. if xs, ok := extensionCache.LoadOrStore(s, xs); ok {
  306. return xs.(extensionsByNumber)
  307. }
  308. return xs
  309. }
  310. // printWarning prints a warning to os.Stderr regarding a registration conflict.
  311. func printWarning(err error) {
  312. // TODO: Provide a link in the warning to a page that explains
  313. // what the user should do instead?
  314. b := make([]byte, 0, 1<<12)
  315. b = append(b, "==================\n"...)
  316. b = append(b, "WARNING: "+err.Error()+"\n"...)
  317. b = append(b, "A future release of proto will panic on registration conflicts.\n\n"...)
  318. b = b[:len(b)+runtime.Stack(b[len(b):cap(b)], false)]
  319. b = append(b, "==================\n"...)
  320. os.Stderr.Write(b)
  321. }