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