blob: 9919cdb163e0d9b99fcd12b333189e3c87dd5367 [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 Tsai08e00302018-11-26 22:32:06 -08005package legacy
Joe Tsai90fe9962018-10-18 11:06:29 -07006
7import (
8 "fmt"
Joe Tsai90fe9962018-10-18 11:06:29 -07009 "reflect"
Joe Tsai90fe9962018-10-18 11:06:29 -070010 "strings"
11 "sync"
12 "unicode"
13
Damien Neile89e6242019-05-13 23:55:40 -070014 ptag "google.golang.org/protobuf/internal/encoding/tag"
15 pimpl "google.golang.org/protobuf/internal/impl"
16 ptype "google.golang.org/protobuf/internal/prototype"
17 pref "google.golang.org/protobuf/reflect/protoreflect"
Joe Tsai90fe9962018-10-18 11:06:29 -070018)
19
Joe Tsai6dbffb72018-12-04 14:06:19 -080020// wrapMessage wraps v as a protoreflect.ProtoMessage,
Joe Tsaif0c01e42018-11-06 13:05:20 -080021// where v must be a *struct kind and not implement the v2 API already.
Joe Tsai6dbffb72018-12-04 14:06:19 -080022func wrapMessage(v reflect.Value) pref.ProtoMessage {
Joe Tsai4fe96632019-05-22 05:12:36 -040023 mt := loadMessageInfo(v.Type())
Joe Tsai08e00302018-11-26 22:32:06 -080024 return mt.MessageOf(v.Interface()).Interface()
Joe Tsaif0c01e42018-11-06 13:05:20 -080025}
26
Joe Tsai4fe96632019-05-22 05:12:36 -040027var messageTypeCache sync.Map // map[reflect.Type]*MessageInfo
Joe Tsaice6edd32018-10-19 16:27:46 -070028
Joe Tsai4fe96632019-05-22 05:12:36 -040029// loadMessageInfo dynamically loads a *MessageInfo for t,
Joe Tsaif0c01e42018-11-06 13:05:20 -080030// where t must be a *struct kind and not implement the v2 API already.
Joe Tsai4fe96632019-05-22 05:12:36 -040031func loadMessageInfo(t reflect.Type) *pimpl.MessageInfo {
32 // Fast-path: check if a MessageInfo is cached for this concrete type.
Joe Tsaif0c01e42018-11-06 13:05:20 -080033 if mt, ok := messageTypeCache.Load(t); ok {
Joe Tsai4fe96632019-05-22 05:12:36 -040034 return mt.(*pimpl.MessageInfo)
Joe Tsaice6edd32018-10-19 16:27:46 -070035 }
36
Joe Tsai4fe96632019-05-22 05:12:36 -040037 // Slow-path: derive message descriptor and initialize MessageInfo.
Joe Tsai35ec98f2019-03-25 14:41:32 -070038 md := LoadMessageDesc(t)
Joe Tsai4fe96632019-05-22 05:12:36 -040039 mt := new(pimpl.MessageInfo)
Damien Neil8012b442019-01-18 09:32:24 -080040 mt.GoType = t
41 mt.PBType = ptype.GoMessage(md, func(pref.MessageType) pref.Message {
Joe Tsaif0c01e42018-11-06 13:05:20 -080042 p := reflect.New(t.Elem()).Interface()
Joe Tsai3bc7d6f2019-01-09 02:57:13 -080043 return mt.MessageOf(p)
Joe Tsaif0c01e42018-11-06 13:05:20 -080044 })
Joe Tsaib9365042019-03-19 14:14:29 -070045 if mt, ok := messageTypeCache.LoadOrStore(t, mt); ok {
Joe Tsai4fe96632019-05-22 05:12:36 -040046 return mt.(*pimpl.MessageInfo)
Joe Tsaib9365042019-03-19 14:14:29 -070047 }
Joe Tsaif0c01e42018-11-06 13:05:20 -080048 return mt
Joe Tsaice6edd32018-10-19 16:27:46 -070049}
50
Joe Tsaib9365042019-03-19 14:14:29 -070051var messageDescLock sync.Mutex
Joe Tsai90fe9962018-10-18 11:06:29 -070052var messageDescCache sync.Map // map[reflect.Type]protoreflect.MessageDescriptor
53
Joe Tsai35ec98f2019-03-25 14:41:32 -070054// LoadMessageDesc returns an MessageDescriptor derived from the Go type,
Joe Tsaice6edd32018-10-19 16:27:46 -070055// which must be a *struct kind and not implement the v2 API already.
Joe Tsai35ec98f2019-03-25 14:41:32 -070056//
57// This is exported for testing purposes.
58func LoadMessageDesc(t reflect.Type) pref.MessageDescriptor {
Joe Tsai90fe9962018-10-18 11:06:29 -070059 return messageDescSet{}.Load(t)
60}
61
62type messageDescSet struct {
63 visited map[reflect.Type]*ptype.StandaloneMessage
64 descs []*ptype.StandaloneMessage
65 types []reflect.Type
66}
67
68func (ms messageDescSet) Load(t reflect.Type) pref.MessageDescriptor {
69 // Fast-path: check if a MessageDescriptor is cached for this concrete type.
70 if mi, ok := messageDescCache.Load(t); ok {
71 return mi.(pref.MessageDescriptor)
72 }
73
74 // Slow-path: initialize MessageDescriptor from the Go type.
Joe Tsaib9365042019-03-19 14:14:29 -070075 //
76 // Hold a global lock during message creation to ensure that each Go type
77 // maps to exactly one MessageDescriptor. After obtaining the lock, we must
78 // check again whether the message has already been handled.
79 messageDescLock.Lock()
80 defer messageDescLock.Unlock()
81 if mi, ok := messageDescCache.Load(t); ok {
82 return mi.(pref.MessageDescriptor)
83 }
Joe Tsai90fe9962018-10-18 11:06:29 -070084
85 // Processing t recursively populates descs and types with all sub-messages.
86 // The descriptor for the first type is guaranteed to be at the front.
87 ms.processMessage(t)
88
89 // Within a proto file it is possible for cyclic dependencies to exist
90 // between multiple message types. When these cases arise, the set of
91 // message descriptors must be created together.
92 mds, err := ptype.NewMessages(ms.descs)
93 if err != nil {
94 panic(err)
95 }
96 for i, md := range mds {
97 // Protobuf semantics represents map entries under-the-hood as
98 // pseudo-messages (has a descriptor, but no generated Go type).
99 // Avoid caching these fake messages.
100 if t := ms.types[i]; t.Kind() != reflect.Map {
101 messageDescCache.Store(t, md)
102 }
103 }
104 return mds[0]
105}
106
107func (ms *messageDescSet) processMessage(t reflect.Type) pref.MessageDescriptor {
108 // Fast-path: Obtain a placeholder if the message is already processed.
109 if m, ok := ms.visited[t]; ok {
110 return ptype.PlaceholderMessage(m.FullName)
111 }
112
113 // Slow-path: Walk over the struct fields to derive the message descriptor.
Joe Tsai6f9095c2018-11-10 14:12:21 -0800114 if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct || t.Elem().PkgPath() == "" {
115 panic(fmt.Sprintf("got %v, want named *struct kind", t))
Joe Tsai90fe9962018-10-18 11:06:29 -0700116 }
117
118 // Derive name and syntax from the raw descriptor.
119 m := new(ptype.StandaloneMessage)
120 mv := reflect.New(t.Elem()).Interface()
121 if _, ok := mv.(pref.ProtoMessage); ok {
122 panic(fmt.Sprintf("%v already implements proto.Message", t))
123 }
Joe Tsai6dbffb72018-12-04 14:06:19 -0800124 if md, ok := mv.(messageV1); ok {
Joe Tsai90fe9962018-10-18 11:06:29 -0700125 b, idxs := md.Descriptor()
Damien Neil987d5702019-04-10 11:06:53 -0700126 fd := loadFileDesc(b)
Joe Tsai90fe9962018-10-18 11:06:29 -0700127
128 // Derive syntax.
129 switch fd.GetSyntax() {
130 case "proto2", "":
131 m.Syntax = pref.Proto2
132 case "proto3":
133 m.Syntax = pref.Proto3
134 }
135
136 // Derive full name.
137 md := fd.MessageType[idxs[0]]
138 m.FullName = pref.FullName(fd.GetPackage()).Append(pref.Name(md.GetName()))
139 for _, i := range idxs[1:] {
140 md = md.NestedType[i]
141 m.FullName = m.FullName.Append(pref.Name(md.GetName()))
142 }
143 } else {
Joe Tsai6dbffb72018-12-04 14:06:19 -0800144 // If the type does not implement messageV1, then the only way to
Joe Tsai90fe9962018-10-18 11:06:29 -0700145 // obtain the full name is through the registry. However, this is
146 // unreliable as some generated messages register with a fork of
147 // golang/protobuf, so the registry may not have this information.
148 m.FullName = deriveFullName(t.Elem())
149 m.Syntax = pref.Proto2
150
151 // Try to determine if the message is using proto3 by checking scalars.
152 for i := 0; i < t.Elem().NumField(); i++ {
153 f := t.Elem().Field(i)
154 if tag := f.Tag.Get("protobuf"); tag != "" {
155 switch f.Type.Kind() {
156 case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
157 m.Syntax = pref.Proto3
158 }
159 for _, s := range strings.Split(tag, ",") {
160 if s == "proto3" {
161 m.Syntax = pref.Proto3
162 }
163 }
164 }
165 }
166 }
167 ms.visit(m, t)
168
169 // Obtain a list of oneof wrapper types.
170 var oneofWrappers []reflect.Type
171 if fn, ok := t.MethodByName("XXX_OneofFuncs"); ok {
172 vs := fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[3]
173 for _, v := range vs.Interface().([]interface{}) {
174 oneofWrappers = append(oneofWrappers, reflect.TypeOf(v))
175 }
176 }
Joe Tsai25cc69d2018-11-28 23:43:49 -0800177 if fn, ok := t.MethodByName("XXX_OneofWrappers"); ok {
178 vs := fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0]
179 for _, v := range vs.Interface().([]interface{}) {
180 oneofWrappers = append(oneofWrappers, reflect.TypeOf(v))
181 }
182 }
Joe Tsai90fe9962018-10-18 11:06:29 -0700183
184 // Obtain a list of the extension ranges.
185 if fn, ok := t.MethodByName("ExtensionRangeArray"); ok {
186 vs := fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0]
187 for i := 0; i < vs.Len(); i++ {
188 v := vs.Index(i)
189 m.ExtensionRanges = append(m.ExtensionRanges, [2]pref.FieldNumber{
190 pref.FieldNumber(v.FieldByName("Start").Int()),
191 pref.FieldNumber(v.FieldByName("End").Int() + 1),
192 })
193 }
194 }
195
196 // Derive the message fields by inspecting the struct fields.
197 for i := 0; i < t.Elem().NumField(); i++ {
198 f := t.Elem().Field(i)
199 if tag := f.Tag.Get("protobuf"); tag != "" {
200 tagKey := f.Tag.Get("protobuf_key")
201 tagVal := f.Tag.Get("protobuf_val")
202 m.Fields = append(m.Fields, ms.parseField(tag, tagKey, tagVal, f.Type, m))
203 }
204 if tag := f.Tag.Get("protobuf_oneof"); tag != "" {
205 name := pref.Name(tag)
206 m.Oneofs = append(m.Oneofs, ptype.Oneof{Name: name})
207 for _, t := range oneofWrappers {
208 if t.Implements(f.Type) {
209 f := t.Elem().Field(0)
210 if tag := f.Tag.Get("protobuf"); tag != "" {
211 ft := ms.parseField(tag, "", "", f.Type, m)
212 ft.OneofName = name
213 m.Fields = append(m.Fields, ft)
214 }
215 }
216 }
217 }
218 }
219
220 return ptype.PlaceholderMessage(m.FullName)
221}
222
Joe Tsai05828db2018-11-01 13:52:16 -0700223func (ms *messageDescSet) parseField(tag, tagKey, tagVal string, goType reflect.Type, parent *ptype.StandaloneMessage) ptype.Field {
224 t := goType
Joe Tsai90fe9962018-10-18 11:06:29 -0700225 isOptional := t.Kind() == reflect.Ptr && t.Elem().Kind() != reflect.Struct
226 isRepeated := t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
227 if isOptional || isRepeated {
228 t = t.Elem()
229 }
Joe Tsai05828db2018-11-01 13:52:16 -0700230 f := ptag.Unmarshal(tag, t)
Joe Tsai90fe9962018-10-18 11:06:29 -0700231
232 // Populate EnumType and MessageType.
233 if f.EnumType == nil && f.Kind == pref.EnumKind {
Damien Neila8593ba2019-01-08 16:18:07 -0800234 if ev, ok := reflect.Zero(t).Interface().(pref.Enum); ok {
Joe Tsai0fc49f82019-05-01 12:29:25 -0700235 f.EnumType = ev.Descriptor()
Joe Tsai90fe9962018-10-18 11:06:29 -0700236 } else {
Joe Tsai35ec98f2019-03-25 14:41:32 -0700237 f.EnumType = LoadEnumDesc(t)
Joe Tsai90fe9962018-10-18 11:06:29 -0700238 }
239 }
240 if f.MessageType == nil && (f.Kind == pref.MessageKind || f.Kind == pref.GroupKind) {
241 if mv, ok := reflect.Zero(t).Interface().(pref.ProtoMessage); ok {
Joe Tsai0fc49f82019-05-01 12:29:25 -0700242 f.MessageType = mv.ProtoReflect().Descriptor()
Joe Tsai90fe9962018-10-18 11:06:29 -0700243 } else if t.Kind() == reflect.Map {
244 m := &ptype.StandaloneMessage{
Damien Neil232ea152018-12-10 15:14:36 -0800245 Syntax: parent.Syntax,
246 FullName: parent.FullName.Append(mapEntryName(f.Name)),
Damien Neil232ea152018-12-10 15:14:36 -0800247 IsMapEntry: true,
Joe Tsai90fe9962018-10-18 11:06:29 -0700248 Fields: []ptype.Field{
249 ms.parseField(tagKey, "", "", t.Key(), nil),
250 ms.parseField(tagVal, "", "", t.Elem(), nil),
251 },
252 }
253 ms.visit(m, t)
254 f.MessageType = ptype.PlaceholderMessage(m.FullName)
255 } else if mv, ok := messageDescCache.Load(t); ok {
256 f.MessageType = mv.(pref.MessageDescriptor)
257 } else {
258 f.MessageType = ms.processMessage(t)
259 }
260 }
261 return f
262}
263
264func (ms *messageDescSet) visit(m *ptype.StandaloneMessage, t reflect.Type) {
265 if ms.visited == nil {
266 ms.visited = make(map[reflect.Type]*ptype.StandaloneMessage)
267 }
268 if t.Kind() != reflect.Map {
269 ms.visited[t] = m
270 }
271 ms.descs = append(ms.descs, m)
272 ms.types = append(ms.types, t)
273}
274
275// deriveFullName derives a fully qualified protobuf name for the given Go type
276// The provided name is not guaranteed to be stable nor universally unique.
277// It should be sufficiently unique within a program.
278func deriveFullName(t reflect.Type) pref.FullName {
279 sanitize := func(r rune) rune {
280 switch {
281 case r == '/':
282 return '.'
283 case 'a' <= r && r <= 'z', 'A' <= r && r <= 'Z', '0' <= r && r <= '9':
284 return r
285 default:
286 return '_'
287 }
288 }
289 prefix := strings.Map(sanitize, t.PkgPath())
290 suffix := strings.Map(sanitize, t.Name())
291 if suffix == "" {
292 suffix = fmt.Sprintf("UnknownX%X", reflect.ValueOf(t).Pointer())
293 }
294
295 ss := append(strings.Split(prefix, "."), suffix)
296 for i, s := range ss {
297 if s == "" || ('0' <= s[0] && s[0] <= '9') {
298 ss[i] = "x" + s
299 }
300 }
301 return pref.FullName(strings.Join(ss, "."))
302}
303
304// mapEntryName derives the message name for a map field of a given name.
305// This is identical to MapEntryName from parser.cc in the protoc source.
306func mapEntryName(s pref.Name) pref.Name {
307 var b []byte
308 nextUpper := true
309 for i := 0; i < len(s); i++ {
310 if c := s[i]; c == '_' {
311 nextUpper = true
312 } else {
313 if nextUpper {
314 c = byte(unicode.ToUpper(rune(c)))
315 nextUpper = false
316 }
317 b = append(b, c)
318 }
319 }
320 return pref.Name(append(b, "Entry"...))
321}