legacy_message.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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 impl
  5. import (
  6. "fmt"
  7. "reflect"
  8. "strings"
  9. "sync"
  10. "unicode"
  11. "google.golang.org/protobuf/internal/descopts"
  12. ptag "google.golang.org/protobuf/internal/encoding/tag"
  13. "google.golang.org/protobuf/internal/filedesc"
  14. "google.golang.org/protobuf/reflect/protoreflect"
  15. pref "google.golang.org/protobuf/reflect/protoreflect"
  16. "google.golang.org/protobuf/reflect/prototype"
  17. )
  18. // legacyWrapMessage wraps v as a protoreflect.ProtoMessage,
  19. // where v must be a *struct kind and not implement the v2 API already.
  20. func legacyWrapMessage(v reflect.Value) pref.ProtoMessage {
  21. mt := legacyLoadMessageInfo(v.Type())
  22. return mt.MessageOf(v.Interface()).Interface()
  23. }
  24. var legacyMessageTypeCache sync.Map // map[reflect.Type]*MessageInfo
  25. // legacyLoadMessageInfo dynamically loads a *MessageInfo for t,
  26. // where t must be a *struct kind and not implement the v2 API already.
  27. func legacyLoadMessageInfo(t reflect.Type) *MessageInfo {
  28. // Fast-path: check if a MessageInfo is cached for this concrete type.
  29. if mt, ok := legacyMessageTypeCache.Load(t); ok {
  30. return mt.(*MessageInfo)
  31. }
  32. // Slow-path: derive message descriptor and initialize MessageInfo.
  33. md := LegacyLoadMessageDesc(t)
  34. mt := new(MessageInfo)
  35. mt.GoType = t
  36. mt.PBType = &prototype.Message{
  37. MessageDescriptor: md,
  38. NewMessage: func() pref.Message {
  39. return mt.MessageOf(reflect.New(t.Elem()).Interface())
  40. },
  41. }
  42. if mt, ok := legacyMessageTypeCache.LoadOrStore(t, mt); ok {
  43. return mt.(*MessageInfo)
  44. }
  45. return mt
  46. }
  47. var legacyMessageDescCache sync.Map // map[reflect.Type]protoreflect.MessageDescriptor
  48. // LegacyLoadMessageDesc returns an MessageDescriptor derived from the Go type,
  49. // which must be a *struct kind and not implement the v2 API already.
  50. //
  51. // This is exported for testing purposes.
  52. func LegacyLoadMessageDesc(t reflect.Type) pref.MessageDescriptor {
  53. return legacyLoadMessageDesc(t, true)
  54. }
  55. func legacyLoadMessageDesc(t reflect.Type, finalized bool) pref.MessageDescriptor {
  56. // Fast-path: check if a MessageDescriptor is cached for this concrete type.
  57. if mi, ok := legacyMessageDescCache.Load(t); ok {
  58. return mi.(pref.MessageDescriptor)
  59. }
  60. // Slow-path: initialize MessageDescriptor from the raw descriptor.
  61. mv := reflect.New(t.Elem()).Interface()
  62. if _, ok := mv.(pref.ProtoMessage); ok {
  63. panic(fmt.Sprintf("%v already implements proto.Message", t))
  64. }
  65. mdV1, ok := mv.(messageV1)
  66. if !ok {
  67. return aberrantLoadMessageDesc(t, finalized)
  68. }
  69. b, idxs := mdV1.Descriptor()
  70. md := legacyLoadFileDesc(b).Messages().Get(idxs[0])
  71. for _, i := range idxs[1:] {
  72. md = md.Messages().Get(i)
  73. }
  74. if md, ok := legacyMessageDescCache.LoadOrStore(t, md); ok {
  75. return md.(protoreflect.MessageDescriptor)
  76. }
  77. return md
  78. }
  79. var aberrantMessageDescCache sync.Map // map[reflect.Type]aberrantMessageDesc
  80. // aberrantMessageDesc is a tuple containing a MessageDescriptor and a channel
  81. // to signal whether the descriptor is initialized. For external lookups,
  82. // we must ensure that the descriptor is fully initialized. For internal lookups
  83. // to resolve cycles, we only need to obtain the descriptor reference.
  84. type aberrantMessageDesc struct {
  85. desc protoreflect.MessageDescriptor
  86. done chan struct{} // closed when desc is fully initialized
  87. }
  88. // aberrantLoadEnumDesc returns an EnumDescriptor derived from the Go type,
  89. // which must not implement protoreflect.ProtoMessage or messageV1.
  90. //
  91. // This is a best-effort derivation of the message descriptor using the protobuf
  92. // tags on the struct fields.
  93. //
  94. // The finalized flag determines whether the returned message descriptor must
  95. // be fully initialized.
  96. func aberrantLoadMessageDesc(t reflect.Type, finalized bool) pref.MessageDescriptor {
  97. // Fast-path: check if an MessageDescriptor is cached for this concrete type.
  98. if mdi, ok := aberrantMessageDescCache.Load(t); ok {
  99. if finalized {
  100. <-mdi.(aberrantMessageDesc).done
  101. }
  102. return mdi.(aberrantMessageDesc).desc
  103. }
  104. // Medium-path: create an initial descriptor and cache it immediately,
  105. // so that cyclic references can be resolved. Each descriptor is paired
  106. // with a channel to signal when the descriptor is fully initialized.
  107. md := &filedesc.Message{L2: new(filedesc.MessageL2)}
  108. mdi := aberrantMessageDesc{desc: md, done: make(chan struct{})}
  109. if mdi, ok := aberrantMessageDescCache.LoadOrStore(t, mdi); ok {
  110. if finalized {
  111. <-mdi.(aberrantMessageDesc).done
  112. }
  113. return mdi.(aberrantMessageDesc).desc
  114. }
  115. defer func() { close(mdi.done) }()
  116. // Slow-path: construct a descriptor from the Go struct type (best-effort).
  117. md.L0.FullName = aberrantDeriveFullName(t.Elem())
  118. md.L0.ParentFile = filedesc.SurrogateProto2
  119. // Try to determine if the message is using proto3 by checking scalars.
  120. for i := 0; i < t.Elem().NumField(); i++ {
  121. f := t.Elem().Field(i)
  122. if tag := f.Tag.Get("protobuf"); tag != "" {
  123. switch f.Type.Kind() {
  124. case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
  125. md.L0.ParentFile = filedesc.SurrogateProto3
  126. }
  127. for _, s := range strings.Split(tag, ",") {
  128. if s == "proto3" {
  129. md.L0.ParentFile = filedesc.SurrogateProto3
  130. }
  131. }
  132. }
  133. }
  134. // Obtain a list of oneof wrapper types.
  135. var oneofWrappers []reflect.Type
  136. if fn, ok := t.MethodByName("XXX_OneofFuncs"); ok {
  137. vs := fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[3]
  138. for _, v := range vs.Interface().([]interface{}) {
  139. oneofWrappers = append(oneofWrappers, reflect.TypeOf(v))
  140. }
  141. }
  142. if fn, ok := t.MethodByName("XXX_OneofWrappers"); ok {
  143. vs := fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0]
  144. for _, v := range vs.Interface().([]interface{}) {
  145. oneofWrappers = append(oneofWrappers, reflect.TypeOf(v))
  146. }
  147. }
  148. // Obtain a list of the extension ranges.
  149. if fn, ok := t.MethodByName("ExtensionRangeArray"); ok {
  150. vs := fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0]
  151. for i := 0; i < vs.Len(); i++ {
  152. v := vs.Index(i)
  153. md.L2.ExtensionRanges.List = append(md.L2.ExtensionRanges.List, [2]pref.FieldNumber{
  154. pref.FieldNumber(v.FieldByName("Start").Int()),
  155. pref.FieldNumber(v.FieldByName("End").Int() + 1),
  156. })
  157. md.L2.ExtensionRangeOptions = append(md.L2.ExtensionRangeOptions, nil)
  158. }
  159. }
  160. // Derive the message fields by inspecting the struct fields.
  161. for i := 0; i < t.Elem().NumField(); i++ {
  162. f := t.Elem().Field(i)
  163. if tag := f.Tag.Get("protobuf"); tag != "" {
  164. tagKey := f.Tag.Get("protobuf_key")
  165. tagVal := f.Tag.Get("protobuf_val")
  166. aberrantAppendField(md, f.Type, tag, tagKey, tagVal)
  167. }
  168. if tag := f.Tag.Get("protobuf_oneof"); tag != "" {
  169. n := len(md.L2.Oneofs.List)
  170. md.L2.Oneofs.List = append(md.L2.Oneofs.List, filedesc.Oneof{})
  171. od := &md.L2.Oneofs.List[n]
  172. od.L0.FullName = md.FullName().Append(pref.Name(tag))
  173. od.L0.ParentFile = md.L0.ParentFile
  174. od.L0.Parent = md
  175. od.L0.Index = n
  176. for _, t := range oneofWrappers {
  177. if t.Implements(f.Type) {
  178. f := t.Elem().Field(0)
  179. if tag := f.Tag.Get("protobuf"); tag != "" {
  180. aberrantAppendField(md, f.Type, tag, "", "")
  181. fd := &md.L2.Fields.List[len(md.L2.Fields.List)-1]
  182. fd.L1.ContainingOneof = od
  183. od.L1.Fields.List = append(od.L1.Fields.List, fd)
  184. }
  185. }
  186. }
  187. }
  188. }
  189. // TODO: Use custom Marshal/Unmarshal methods for the fast-path?
  190. return md
  191. }
  192. func aberrantAppendField(md *filedesc.Message, goType reflect.Type, tag, tagKey, tagVal string) {
  193. t := goType
  194. isOptional := t.Kind() == reflect.Ptr && t.Elem().Kind() != reflect.Struct
  195. isRepeated := t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
  196. if isOptional || isRepeated {
  197. t = t.Elem()
  198. }
  199. fd := ptag.Unmarshal(tag, t, placeholderEnumValues{}).(*filedesc.Field)
  200. // Append field descriptor to the message.
  201. n := len(md.L2.Fields.List)
  202. md.L2.Fields.List = append(md.L2.Fields.List, *fd)
  203. fd = &md.L2.Fields.List[n]
  204. fd.L0.FullName = md.FullName().Append(fd.Name())
  205. fd.L0.ParentFile = md.L0.ParentFile
  206. fd.L0.Parent = md
  207. fd.L0.Index = n
  208. if fd.L1.IsWeak || fd.L1.HasPacked {
  209. fd.L1.Options = func() pref.ProtoMessage {
  210. opts := descopts.Field.ProtoReflect().New()
  211. if fd.L1.IsWeak {
  212. opts.Set(opts.Descriptor().Fields().ByName("weak"), protoreflect.ValueOf(true))
  213. }
  214. if fd.L1.HasPacked {
  215. opts.Set(opts.Descriptor().Fields().ByName("packed"), protoreflect.ValueOf(fd.L1.IsPacked))
  216. }
  217. return opts.Interface()
  218. }
  219. }
  220. // Populate Enum and Message.
  221. if fd.Enum() == nil && fd.Kind() == pref.EnumKind {
  222. switch v := reflect.Zero(t).Interface().(type) {
  223. case pref.Enum:
  224. fd.L1.Enum = v.Descriptor()
  225. default:
  226. fd.L1.Enum = LegacyLoadEnumDesc(t)
  227. }
  228. }
  229. if fd.Message() == nil && (fd.Kind() == pref.MessageKind || fd.Kind() == pref.GroupKind) {
  230. switch v := reflect.Zero(t).Interface().(type) {
  231. case pref.ProtoMessage:
  232. fd.L1.Message = v.ProtoReflect().Descriptor()
  233. default:
  234. if t.Kind() == reflect.Map {
  235. n := len(md.L1.Messages.List)
  236. md.L1.Messages.List = append(md.L1.Messages.List, filedesc.Message{L2: new(filedesc.MessageL2)})
  237. md2 := &md.L1.Messages.List[n]
  238. md2.L0.FullName = md.FullName().Append(aberrantMapEntryName(fd.Name()))
  239. md2.L0.ParentFile = md.L0.ParentFile
  240. md2.L0.Parent = md
  241. md2.L0.Index = n
  242. md2.L2.IsMapEntry = true
  243. md2.L2.Options = func() pref.ProtoMessage {
  244. opts := descopts.Message.ProtoReflect().New()
  245. opts.Set(opts.Descriptor().Fields().ByName("map_entry"), protoreflect.ValueOf(true))
  246. return opts.Interface()
  247. }
  248. aberrantAppendField(md2, t.Key(), tagKey, "", "")
  249. aberrantAppendField(md2, t.Elem(), tagVal, "", "")
  250. fd.L1.Message = md2
  251. break
  252. }
  253. fd.L1.Message = aberrantLoadMessageDesc(t, false)
  254. }
  255. }
  256. }
  257. type placeholderEnumValues struct {
  258. protoreflect.EnumValueDescriptors
  259. }
  260. func (placeholderEnumValues) ByNumber(n pref.EnumNumber) pref.EnumValueDescriptor {
  261. return filedesc.PlaceholderEnumValue(pref.FullName(fmt.Sprintf("UNKNOWN_%d", n)))
  262. }
  263. // aberrantMapEntryName derives the name for a map entry message.
  264. // See protoc v3.8.0: src/google/protobuf/descriptor.cc:254-276,6057
  265. func aberrantMapEntryName(s pref.Name) pref.Name {
  266. var b []byte
  267. upperNext := true
  268. for _, c := range s {
  269. switch {
  270. case c == '_':
  271. upperNext = true
  272. case upperNext:
  273. b = append(b, byte(unicode.ToUpper(c)))
  274. upperNext = false
  275. default:
  276. b = append(b, byte(c))
  277. }
  278. }
  279. b = append(b, "Entry"...)
  280. return pref.Name(b)
  281. }