blob: 703e86542baa83f483046dd35248813e865be3df [file] [log] [blame]
Joe Tsai90fe9962018-10-18 11:06:29 -07001// 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
Joe Tsai21ade492019-05-22 13:42:54 -04005package impl
Joe Tsai90fe9962018-10-18 11:06:29 -07006
7import (
8 "fmt"
Joe Tsai90fe9962018-10-18 11:06:29 -07009 "reflect"
Joe Tsai851185d2019-07-01 13:45:52 -070010 "strings"
Joe Tsai90fe9962018-10-18 11:06:29 -070011 "sync"
Joe Tsai90fe9962018-10-18 11:06:29 -070012
Joe Tsai851185d2019-07-01 13:45:52 -070013 "google.golang.org/protobuf/internal/descopts"
14 ptag "google.golang.org/protobuf/internal/encoding/tag"
15 "google.golang.org/protobuf/internal/filedesc"
Joe Tsai97a87392019-07-07 01:49:59 -070016 "google.golang.org/protobuf/internal/strs"
Joe Tsaid8881392019-06-06 13:01:53 -070017 "google.golang.org/protobuf/reflect/protoreflect"
Damien Neile89e6242019-05-13 23:55:40 -070018 pref "google.golang.org/protobuf/reflect/protoreflect"
Damien Neil37ef6912019-09-25 16:51:15 -070019 piface "google.golang.org/protobuf/runtime/protoiface"
Joe Tsai90fe9962018-10-18 11:06:29 -070020)
21
Joe Tsai21ade492019-05-22 13:42:54 -040022// legacyWrapMessage wraps v as a protoreflect.ProtoMessage,
Joe Tsaif0c01e42018-11-06 13:05:20 -080023// where v must be a *struct kind and not implement the v2 API already.
Joe Tsai21ade492019-05-22 13:42:54 -040024func legacyWrapMessage(v reflect.Value) pref.ProtoMessage {
Joe Tsaiea5ada12019-09-04 22:41:40 -070025 mt := legacyLoadMessageInfo(v.Type(), "")
Joe Tsai08e00302018-11-26 22:32:06 -080026 return mt.MessageOf(v.Interface()).Interface()
Joe Tsaif0c01e42018-11-06 13:05:20 -080027}
28
Joe Tsai21ade492019-05-22 13:42:54 -040029var legacyMessageTypeCache sync.Map // map[reflect.Type]*MessageInfo
Joe Tsaice6edd32018-10-19 16:27:46 -070030
Joe Tsai21ade492019-05-22 13:42:54 -040031// legacyLoadMessageInfo dynamically loads a *MessageInfo for t,
Joe Tsaif0c01e42018-11-06 13:05:20 -080032// where t must be a *struct kind and not implement the v2 API already.
Joe Tsaiea5ada12019-09-04 22:41:40 -070033// The provided name is used if it cannot be determined from the message.
34func legacyLoadMessageInfo(t reflect.Type, name pref.FullName) *MessageInfo {
Joe Tsai4fe96632019-05-22 05:12:36 -040035 // Fast-path: check if a MessageInfo is cached for this concrete type.
Joe Tsai21ade492019-05-22 13:42:54 -040036 if mt, ok := legacyMessageTypeCache.Load(t); ok {
37 return mt.(*MessageInfo)
Joe Tsaice6edd32018-10-19 16:27:46 -070038 }
39
Joe Tsai4fe96632019-05-22 05:12:36 -040040 // Slow-path: derive message descriptor and initialize MessageInfo.
Damien Neil16163b42019-08-06 15:43:25 -070041 mi := &MessageInfo{
Joe Tsaiea5ada12019-09-04 22:41:40 -070042 Desc: legacyLoadMessageDesc(t, name),
Damien Neil16163b42019-08-06 15:43:25 -070043 GoReflectType: t,
Joe Tsaib2f66be2019-05-22 00:42:45 -040044 }
Damien Neil37ef6912019-09-25 16:51:15 -070045
46 v := reflect.Zero(t).Interface()
47 type marshaler interface {
48 Marshal() ([]byte, error)
49 }
50 if _, ok := v.(marshaler); ok {
51 mi.methods.MarshalAppend = func(b []byte, m pref.Message, _ piface.MarshalOptions) ([]byte, error) {
52 out, err := m.Interface().(unwrapper).protoUnwrap().(marshaler).Marshal()
53 if b != nil {
54 out = append(b, out...)
55 }
56 return out, err
57 }
58 mi.methods.Size = func(m pref.Message, _ piface.MarshalOptions) int {
59 // This is not at all efficient.
60 b, _ := m.Interface().(unwrapper).protoUnwrap().(marshaler).Marshal()
61 return len(b)
62 }
63 }
64 type unmarshaler interface {
65 Unmarshal([]byte) error
66 }
67 if _, ok := v.(unmarshaler); ok {
68 mi.methods.Unmarshal = func(b []byte, m pref.Message, _ piface.UnmarshalOptions) error {
69 return m.Interface().(unwrapper).protoUnwrap().(unmarshaler).Unmarshal(b)
70 }
71 }
72
Damien Neil16163b42019-08-06 15:43:25 -070073 if mi, ok := legacyMessageTypeCache.LoadOrStore(t, mi); ok {
74 return mi.(*MessageInfo)
Joe Tsaib9365042019-03-19 14:14:29 -070075 }
Damien Neil16163b42019-08-06 15:43:25 -070076 return mi
Joe Tsaice6edd32018-10-19 16:27:46 -070077}
78
Joe Tsaid8881392019-06-06 13:01:53 -070079var legacyMessageDescCache sync.Map // map[reflect.Type]protoreflect.MessageDescriptor
Joe Tsai90fe9962018-10-18 11:06:29 -070080
Joe Tsai21ade492019-05-22 13:42:54 -040081// LegacyLoadMessageDesc returns an MessageDescriptor derived from the Go type,
Joe Tsaice6edd32018-10-19 16:27:46 -070082// which must be a *struct kind and not implement the v2 API already.
Joe Tsai35ec98f2019-03-25 14:41:32 -070083//
84// This is exported for testing purposes.
Joe Tsai21ade492019-05-22 13:42:54 -040085func LegacyLoadMessageDesc(t reflect.Type) pref.MessageDescriptor {
Joe Tsaiea5ada12019-09-04 22:41:40 -070086 return legacyLoadMessageDesc(t, "")
87}
88func legacyLoadMessageDesc(t reflect.Type, name pref.FullName) pref.MessageDescriptor {
Joe Tsai90fe9962018-10-18 11:06:29 -070089 // Fast-path: check if a MessageDescriptor is cached for this concrete type.
Joe Tsai21ade492019-05-22 13:42:54 -040090 if mi, ok := legacyMessageDescCache.Load(t); ok {
Joe Tsai90fe9962018-10-18 11:06:29 -070091 return mi.(pref.MessageDescriptor)
92 }
93
Joe Tsaid8881392019-06-06 13:01:53 -070094 // Slow-path: initialize MessageDescriptor from the raw descriptor.
Joe Tsai90fe9962018-10-18 11:06:29 -070095 mv := reflect.New(t.Elem()).Interface()
96 if _, ok := mv.(pref.ProtoMessage); ok {
97 panic(fmt.Sprintf("%v already implements proto.Message", t))
98 }
Joe Tsaid8881392019-06-06 13:01:53 -070099 mdV1, ok := mv.(messageV1)
100 if !ok {
Joe Tsaiea5ada12019-09-04 22:41:40 -0700101 return aberrantLoadMessageDesc(t, name)
Joe Tsaid8881392019-06-06 13:01:53 -0700102 }
103 b, idxs := mdV1.Descriptor()
Joe Tsai90fe9962018-10-18 11:06:29 -0700104
Joe Tsaid8881392019-06-06 13:01:53 -0700105 md := legacyLoadFileDesc(b).Messages().Get(idxs[0])
106 for _, i := range idxs[1:] {
107 md = md.Messages().Get(i)
Joe Tsai90fe9962018-10-18 11:06:29 -0700108 }
Joe Tsaiea5ada12019-09-04 22:41:40 -0700109 if name != "" && md.FullName() != name {
110 panic(fmt.Sprintf("mismatching message name: got %v, want %v", md.FullName(), name))
111 }
Joe Tsaid8881392019-06-06 13:01:53 -0700112 if md, ok := legacyMessageDescCache.LoadOrStore(t, md); ok {
113 return md.(protoreflect.MessageDescriptor)
Joe Tsai90fe9962018-10-18 11:06:29 -0700114 }
Joe Tsaid8881392019-06-06 13:01:53 -0700115 return md
Joe Tsai90fe9962018-10-18 11:06:29 -0700116}
Joe Tsai851185d2019-07-01 13:45:52 -0700117
Joe Tsai32e8a522019-07-02 10:51:24 -0700118var (
119 aberrantMessageDescLock sync.Mutex
120 aberrantMessageDescCache map[reflect.Type]protoreflect.MessageDescriptor
121)
Joe Tsai851185d2019-07-01 13:45:52 -0700122
Joe Tsaia8044502019-07-06 17:38:44 -0700123// aberrantLoadMessageDesc returns an EnumDescriptor derived from the Go type,
Joe Tsai851185d2019-07-01 13:45:52 -0700124// which must not implement protoreflect.ProtoMessage or messageV1.
125//
126// This is a best-effort derivation of the message descriptor using the protobuf
127// tags on the struct fields.
Joe Tsaiea5ada12019-09-04 22:41:40 -0700128func aberrantLoadMessageDesc(t reflect.Type, name pref.FullName) pref.MessageDescriptor {
Joe Tsai32e8a522019-07-02 10:51:24 -0700129 aberrantMessageDescLock.Lock()
130 defer aberrantMessageDescLock.Unlock()
131 if aberrantMessageDescCache == nil {
132 aberrantMessageDescCache = make(map[reflect.Type]protoreflect.MessageDescriptor)
133 }
Joe Tsaiea5ada12019-09-04 22:41:40 -0700134 return aberrantLoadMessageDescReentrant(t, name)
Joe Tsai32e8a522019-07-02 10:51:24 -0700135}
Joe Tsaiea5ada12019-09-04 22:41:40 -0700136func aberrantLoadMessageDescReentrant(t reflect.Type, name pref.FullName) pref.MessageDescriptor {
Joe Tsai851185d2019-07-01 13:45:52 -0700137 // Fast-path: check if an MessageDescriptor is cached for this concrete type.
Joe Tsai32e8a522019-07-02 10:51:24 -0700138 if md, ok := aberrantMessageDescCache[t]; ok {
139 return md
Joe Tsai851185d2019-07-01 13:45:52 -0700140 }
141
Joe Tsai851185d2019-07-01 13:45:52 -0700142 // Slow-path: construct a descriptor from the Go struct type (best-effort).
Joe Tsai32e8a522019-07-02 10:51:24 -0700143 // Cache the MessageDescriptor early on so that we can resolve internal
144 // cyclic references.
145 md := &filedesc.Message{L2: new(filedesc.MessageL2)}
Joe Tsaiea5ada12019-09-04 22:41:40 -0700146 md.L0.FullName = aberrantDeriveMessageName(t.Elem(), name)
Joe Tsai851185d2019-07-01 13:45:52 -0700147 md.L0.ParentFile = filedesc.SurrogateProto2
Joe Tsai32e8a522019-07-02 10:51:24 -0700148 aberrantMessageDescCache[t] = md
Joe Tsai851185d2019-07-01 13:45:52 -0700149
Joe Tsai851185d2019-07-01 13:45:52 -0700150 // Try to determine if the message is using proto3 by checking scalars.
151 for i := 0; i < t.Elem().NumField(); i++ {
152 f := t.Elem().Field(i)
153 if tag := f.Tag.Get("protobuf"); tag != "" {
154 switch f.Type.Kind() {
155 case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
156 md.L0.ParentFile = filedesc.SurrogateProto3
157 }
158 for _, s := range strings.Split(tag, ",") {
159 if s == "proto3" {
160 md.L0.ParentFile = filedesc.SurrogateProto3
161 }
162 }
163 }
164 }
165
166 // Obtain a list of oneof wrapper types.
167 var oneofWrappers []reflect.Type
168 if fn, ok := t.MethodByName("XXX_OneofFuncs"); ok {
169 vs := fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[3]
170 for _, v := range vs.Interface().([]interface{}) {
171 oneofWrappers = append(oneofWrappers, reflect.TypeOf(v))
172 }
173 }
174 if fn, ok := t.MethodByName("XXX_OneofWrappers"); ok {
175 vs := fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0]
176 for _, v := range vs.Interface().([]interface{}) {
177 oneofWrappers = append(oneofWrappers, reflect.TypeOf(v))
178 }
179 }
180
181 // Obtain a list of the extension ranges.
182 if fn, ok := t.MethodByName("ExtensionRangeArray"); ok {
183 vs := fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0]
184 for i := 0; i < vs.Len(); i++ {
185 v := vs.Index(i)
186 md.L2.ExtensionRanges.List = append(md.L2.ExtensionRanges.List, [2]pref.FieldNumber{
187 pref.FieldNumber(v.FieldByName("Start").Int()),
188 pref.FieldNumber(v.FieldByName("End").Int() + 1),
189 })
190 md.L2.ExtensionRangeOptions = append(md.L2.ExtensionRangeOptions, nil)
191 }
192 }
193
194 // Derive the message fields by inspecting the struct fields.
195 for i := 0; i < t.Elem().NumField(); i++ {
196 f := t.Elem().Field(i)
197 if tag := f.Tag.Get("protobuf"); tag != "" {
198 tagKey := f.Tag.Get("protobuf_key")
199 tagVal := f.Tag.Get("protobuf_val")
200 aberrantAppendField(md, f.Type, tag, tagKey, tagVal)
201 }
202 if tag := f.Tag.Get("protobuf_oneof"); tag != "" {
203 n := len(md.L2.Oneofs.List)
204 md.L2.Oneofs.List = append(md.L2.Oneofs.List, filedesc.Oneof{})
205 od := &md.L2.Oneofs.List[n]
206 od.L0.FullName = md.FullName().Append(pref.Name(tag))
207 od.L0.ParentFile = md.L0.ParentFile
208 od.L0.Parent = md
209 od.L0.Index = n
210
211 for _, t := range oneofWrappers {
212 if t.Implements(f.Type) {
213 f := t.Elem().Field(0)
214 if tag := f.Tag.Get("protobuf"); tag != "" {
215 aberrantAppendField(md, f.Type, tag, "", "")
216 fd := &md.L2.Fields.List[len(md.L2.Fields.List)-1]
217 fd.L1.ContainingOneof = od
218 od.L1.Fields.List = append(od.L1.Fields.List, fd)
219 }
220 }
221 }
222 }
223 }
224
225 // TODO: Use custom Marshal/Unmarshal methods for the fast-path?
226
227 return md
228}
229
Joe Tsaiea5ada12019-09-04 22:41:40 -0700230func aberrantDeriveMessageName(t reflect.Type, name pref.FullName) pref.FullName {
231 if name.IsValid() {
232 return name
233 }
Joe Tsaie87cf532019-09-10 12:20:00 -0700234 func() {
235 defer func() { recover() }() // swallow possible nil panics
236 if m, ok := reflect.New(t).Interface().(interface{ XXX_MessageName() string }); ok {
237 name = pref.FullName(m.XXX_MessageName())
Joe Tsaiea5ada12019-09-04 22:41:40 -0700238 }
Joe Tsaie87cf532019-09-10 12:20:00 -0700239 }()
240 if name.IsValid() {
241 return name
Joe Tsaiea5ada12019-09-04 22:41:40 -0700242 }
243 return aberrantDeriveFullName(t)
244}
245
Joe Tsai851185d2019-07-01 13:45:52 -0700246func aberrantAppendField(md *filedesc.Message, goType reflect.Type, tag, tagKey, tagVal string) {
247 t := goType
248 isOptional := t.Kind() == reflect.Ptr && t.Elem().Kind() != reflect.Struct
249 isRepeated := t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
250 if isOptional || isRepeated {
251 t = t.Elem()
252 }
253 fd := ptag.Unmarshal(tag, t, placeholderEnumValues{}).(*filedesc.Field)
254
255 // Append field descriptor to the message.
256 n := len(md.L2.Fields.List)
257 md.L2.Fields.List = append(md.L2.Fields.List, *fd)
258 fd = &md.L2.Fields.List[n]
259 fd.L0.FullName = md.FullName().Append(fd.Name())
260 fd.L0.ParentFile = md.L0.ParentFile
261 fd.L0.Parent = md
262 fd.L0.Index = n
263
264 if fd.L1.IsWeak || fd.L1.HasPacked {
265 fd.L1.Options = func() pref.ProtoMessage {
266 opts := descopts.Field.ProtoReflect().New()
267 if fd.L1.IsWeak {
Joe Tsai84177c92019-09-17 13:38:48 -0700268 opts.Set(opts.Descriptor().Fields().ByName("weak"), protoreflect.ValueOfBool(true))
Joe Tsai851185d2019-07-01 13:45:52 -0700269 }
270 if fd.L1.HasPacked {
Joe Tsai84177c92019-09-17 13:38:48 -0700271 opts.Set(opts.Descriptor().Fields().ByName("packed"), protoreflect.ValueOfBool(fd.L1.IsPacked))
Joe Tsai851185d2019-07-01 13:45:52 -0700272 }
273 return opts.Interface()
274 }
275 }
276
277 // Populate Enum and Message.
278 if fd.Enum() == nil && fd.Kind() == pref.EnumKind {
279 switch v := reflect.Zero(t).Interface().(type) {
280 case pref.Enum:
281 fd.L1.Enum = v.Descriptor()
282 default:
283 fd.L1.Enum = LegacyLoadEnumDesc(t)
284 }
285 }
286 if fd.Message() == nil && (fd.Kind() == pref.MessageKind || fd.Kind() == pref.GroupKind) {
287 switch v := reflect.Zero(t).Interface().(type) {
288 case pref.ProtoMessage:
289 fd.L1.Message = v.ProtoReflect().Descriptor()
Joe Tsai32e8a522019-07-02 10:51:24 -0700290 case messageV1:
291 fd.L1.Message = LegacyLoadMessageDesc(t)
Joe Tsai851185d2019-07-01 13:45:52 -0700292 default:
293 if t.Kind() == reflect.Map {
294 n := len(md.L1.Messages.List)
295 md.L1.Messages.List = append(md.L1.Messages.List, filedesc.Message{L2: new(filedesc.MessageL2)})
296 md2 := &md.L1.Messages.List[n]
Joe Tsai97a87392019-07-07 01:49:59 -0700297 md2.L0.FullName = md.FullName().Append(pref.Name(strs.MapEntryName(string(fd.Name()))))
Joe Tsai851185d2019-07-01 13:45:52 -0700298 md2.L0.ParentFile = md.L0.ParentFile
299 md2.L0.Parent = md
300 md2.L0.Index = n
301
302 md2.L2.IsMapEntry = true
303 md2.L2.Options = func() pref.ProtoMessage {
304 opts := descopts.Message.ProtoReflect().New()
Joe Tsai84177c92019-09-17 13:38:48 -0700305 opts.Set(opts.Descriptor().Fields().ByName("map_entry"), protoreflect.ValueOfBool(true))
Joe Tsai851185d2019-07-01 13:45:52 -0700306 return opts.Interface()
307 }
308
309 aberrantAppendField(md2, t.Key(), tagKey, "", "")
310 aberrantAppendField(md2, t.Elem(), tagVal, "", "")
311
312 fd.L1.Message = md2
313 break
314 }
Joe Tsaiea5ada12019-09-04 22:41:40 -0700315 fd.L1.Message = aberrantLoadMessageDescReentrant(t, "")
Joe Tsai851185d2019-07-01 13:45:52 -0700316 }
317 }
318}
319
320type placeholderEnumValues struct {
321 protoreflect.EnumValueDescriptors
322}
323
324func (placeholderEnumValues) ByNumber(n pref.EnumNumber) pref.EnumValueDescriptor {
325 return filedesc.PlaceholderEnumValue(pref.FullName(fmt.Sprintf("UNKNOWN_%d", n)))
326}