blob: 3c43bfabfb0356eca16a2e15d55f936b6bfbf974 [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
5package impl
6
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 Neil204f1c02018-10-23 15:03:38 -070014 protoV1 "github.com/golang/protobuf/proto"
15 descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
Joe Tsai05828db2018-11-01 13:52:16 -070016 ptag "github.com/golang/protobuf/v2/internal/encoding/tag"
Joe Tsai6f9095c2018-11-10 14:12:21 -080017 pvalue "github.com/golang/protobuf/v2/internal/value"
Joe Tsai90fe9962018-10-18 11:06:29 -070018 pref "github.com/golang/protobuf/v2/reflect/protoreflect"
19 ptype "github.com/golang/protobuf/v2/reflect/prototype"
20)
21
Joe Tsaice6edd32018-10-19 16:27:46 -070022var messageTypeCache sync.Map // map[reflect.Type]*MessageType
23
Joe Tsai6f9095c2018-11-10 14:12:21 -080024// wrapLegacyMessage wraps v as a protoreflect.ProtoMessage,
25// where v must be a *struct kind and not implement the v2 API already.
26func wrapLegacyMessage(v reflect.Value) pref.ProtoMessage {
Joe Tsaice6edd32018-10-19 16:27:46 -070027 // Fast-path: check if a MessageType is cached for this concrete type.
28 if mt, ok := messageTypeCache.Load(v.Type()); ok {
Joe Tsai6f9095c2018-11-10 14:12:21 -080029 return mt.(*MessageType).MessageOf(v.Interface()).Interface()
Joe Tsaice6edd32018-10-19 16:27:46 -070030 }
31
32 // Slow-path: derive message descriptor and initialize MessageType.
33 mt := &MessageType{Desc: loadMessageDesc(v.Type())}
34 messageTypeCache.Store(v.Type(), mt)
Joe Tsai6f9095c2018-11-10 14:12:21 -080035 return mt.MessageOf(v.Interface()).Interface()
Joe Tsaice6edd32018-10-19 16:27:46 -070036}
37
Joe Tsai6f9095c2018-11-10 14:12:21 -080038type legacyMessageWrapper messageDataType
39
40func (m *legacyMessageWrapper) Type() pref.MessageType {
41 return m.mi.pbType
42}
43func (m *legacyMessageWrapper) KnownFields() pref.KnownFields {
44 return (*knownFields)(m)
45}
46func (m *legacyMessageWrapper) UnknownFields() pref.UnknownFields {
47 return m.mi.unknownFields((*messageDataType)(m))
48}
49func (m *legacyMessageWrapper) Unwrap() interface{} {
50 return m.p.asType(m.mi.goType.Elem()).Interface()
51}
52func (m *legacyMessageWrapper) Interface() pref.ProtoMessage {
53 return m
54}
55func (m *legacyMessageWrapper) ProtoReflect() pref.Message {
56 return m
57}
58func (m *legacyMessageWrapper) ProtoMutable() {}
59
60var (
61 _ pref.Message = (*legacyMessageWrapper)(nil)
62 _ pref.ProtoMessage = (*legacyMessageWrapper)(nil)
63 _ pvalue.Unwrapper = (*legacyMessageWrapper)(nil)
64)
65
Joe Tsai90fe9962018-10-18 11:06:29 -070066var messageDescCache sync.Map // map[reflect.Type]protoreflect.MessageDescriptor
67
68// loadMessageDesc returns an MessageDescriptor derived from the Go type,
Joe Tsaice6edd32018-10-19 16:27:46 -070069// which must be a *struct kind and not implement the v2 API already.
Joe Tsai90fe9962018-10-18 11:06:29 -070070func loadMessageDesc(t reflect.Type) pref.MessageDescriptor {
71 return messageDescSet{}.Load(t)
72}
73
74type messageDescSet struct {
75 visited map[reflect.Type]*ptype.StandaloneMessage
76 descs []*ptype.StandaloneMessage
77 types []reflect.Type
78}
79
80func (ms messageDescSet) Load(t reflect.Type) pref.MessageDescriptor {
81 // Fast-path: check if a MessageDescriptor is cached for this concrete type.
82 if mi, ok := messageDescCache.Load(t); ok {
83 return mi.(pref.MessageDescriptor)
84 }
85
86 // Slow-path: initialize MessageDescriptor from the Go type.
87
88 // Processing t recursively populates descs and types with all sub-messages.
89 // The descriptor for the first type is guaranteed to be at the front.
90 ms.processMessage(t)
91
92 // Within a proto file it is possible for cyclic dependencies to exist
93 // between multiple message types. When these cases arise, the set of
94 // message descriptors must be created together.
95 mds, err := ptype.NewMessages(ms.descs)
96 if err != nil {
97 panic(err)
98 }
99 for i, md := range mds {
100 // Protobuf semantics represents map entries under-the-hood as
101 // pseudo-messages (has a descriptor, but no generated Go type).
102 // Avoid caching these fake messages.
103 if t := ms.types[i]; t.Kind() != reflect.Map {
104 messageDescCache.Store(t, md)
105 }
106 }
107 return mds[0]
108}
109
110func (ms *messageDescSet) processMessage(t reflect.Type) pref.MessageDescriptor {
111 // Fast-path: Obtain a placeholder if the message is already processed.
112 if m, ok := ms.visited[t]; ok {
113 return ptype.PlaceholderMessage(m.FullName)
114 }
115
116 // Slow-path: Walk over the struct fields to derive the message descriptor.
Joe Tsai6f9095c2018-11-10 14:12:21 -0800117 if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct || t.Elem().PkgPath() == "" {
118 panic(fmt.Sprintf("got %v, want named *struct kind", t))
Joe Tsai90fe9962018-10-18 11:06:29 -0700119 }
120
121 // Derive name and syntax from the raw descriptor.
122 m := new(ptype.StandaloneMessage)
123 mv := reflect.New(t.Elem()).Interface()
124 if _, ok := mv.(pref.ProtoMessage); ok {
125 panic(fmt.Sprintf("%v already implements proto.Message", t))
126 }
127 if md, ok := mv.(legacyMessage); ok {
128 b, idxs := md.Descriptor()
129 fd := loadFileDesc(b)
130
131 // Derive syntax.
132 switch fd.GetSyntax() {
133 case "proto2", "":
134 m.Syntax = pref.Proto2
135 case "proto3":
136 m.Syntax = pref.Proto3
137 }
138
139 // Derive full name.
140 md := fd.MessageType[idxs[0]]
141 m.FullName = pref.FullName(fd.GetPackage()).Append(pref.Name(md.GetName()))
142 for _, i := range idxs[1:] {
143 md = md.NestedType[i]
144 m.FullName = m.FullName.Append(pref.Name(md.GetName()))
145 }
146 } else {
147 // If the type does not implement legacyMessage, then the only way to
148 // obtain the full name is through the registry. However, this is
149 // unreliable as some generated messages register with a fork of
150 // golang/protobuf, so the registry may not have this information.
151 m.FullName = deriveFullName(t.Elem())
152 m.Syntax = pref.Proto2
153
154 // Try to determine if the message is using proto3 by checking scalars.
155 for i := 0; i < t.Elem().NumField(); i++ {
156 f := t.Elem().Field(i)
157 if tag := f.Tag.Get("protobuf"); tag != "" {
158 switch f.Type.Kind() {
159 case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
160 m.Syntax = pref.Proto3
161 }
162 for _, s := range strings.Split(tag, ",") {
163 if s == "proto3" {
164 m.Syntax = pref.Proto3
165 }
166 }
167 }
168 }
169 }
170 ms.visit(m, t)
171
172 // Obtain a list of oneof wrapper types.
173 var oneofWrappers []reflect.Type
174 if fn, ok := t.MethodByName("XXX_OneofFuncs"); ok {
175 vs := fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[3]
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 m.ExtensionRanges = append(m.ExtensionRanges, [2]pref.FieldNumber{
187 pref.FieldNumber(v.FieldByName("Start").Int()),
188 pref.FieldNumber(v.FieldByName("End").Int() + 1),
189 })
190 }
191 }
192
193 // Derive the message fields by inspecting the struct fields.
194 for i := 0; i < t.Elem().NumField(); i++ {
195 f := t.Elem().Field(i)
196 if tag := f.Tag.Get("protobuf"); tag != "" {
197 tagKey := f.Tag.Get("protobuf_key")
198 tagVal := f.Tag.Get("protobuf_val")
199 m.Fields = append(m.Fields, ms.parseField(tag, tagKey, tagVal, f.Type, m))
200 }
201 if tag := f.Tag.Get("protobuf_oneof"); tag != "" {
202 name := pref.Name(tag)
203 m.Oneofs = append(m.Oneofs, ptype.Oneof{Name: name})
204 for _, t := range oneofWrappers {
205 if t.Implements(f.Type) {
206 f := t.Elem().Field(0)
207 if tag := f.Tag.Get("protobuf"); tag != "" {
208 ft := ms.parseField(tag, "", "", f.Type, m)
209 ft.OneofName = name
210 m.Fields = append(m.Fields, ft)
211 }
212 }
213 }
214 }
215 }
216
217 return ptype.PlaceholderMessage(m.FullName)
218}
219
Joe Tsai05828db2018-11-01 13:52:16 -0700220func (ms *messageDescSet) parseField(tag, tagKey, tagVal string, goType reflect.Type, parent *ptype.StandaloneMessage) ptype.Field {
221 t := goType
Joe Tsai90fe9962018-10-18 11:06:29 -0700222 isOptional := t.Kind() == reflect.Ptr && t.Elem().Kind() != reflect.Struct
223 isRepeated := t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
224 if isOptional || isRepeated {
225 t = t.Elem()
226 }
Joe Tsai05828db2018-11-01 13:52:16 -0700227 f := ptag.Unmarshal(tag, t)
Joe Tsai90fe9962018-10-18 11:06:29 -0700228
229 // Populate EnumType and MessageType.
230 if f.EnumType == nil && f.Kind == pref.EnumKind {
231 if ev, ok := reflect.Zero(t).Interface().(pref.ProtoEnum); ok {
232 f.EnumType = ev.ProtoReflect().Type()
233 } else {
234 f.EnumType = loadEnumDesc(t)
235 }
236 }
237 if f.MessageType == nil && (f.Kind == pref.MessageKind || f.Kind == pref.GroupKind) {
238 if mv, ok := reflect.Zero(t).Interface().(pref.ProtoMessage); ok {
239 f.MessageType = mv.ProtoReflect().Type()
240 } else if t.Kind() == reflect.Map {
241 m := &ptype.StandaloneMessage{
Damien Neil204f1c02018-10-23 15:03:38 -0700242 Syntax: parent.Syntax,
243 FullName: parent.FullName.Append(mapEntryName(f.Name)),
244 Options: &descriptorV1.MessageOptions{MapEntry: protoV1.Bool(true)},
Joe Tsai90fe9962018-10-18 11:06:29 -0700245 Fields: []ptype.Field{
246 ms.parseField(tagKey, "", "", t.Key(), nil),
247 ms.parseField(tagVal, "", "", t.Elem(), nil),
248 },
249 }
250 ms.visit(m, t)
251 f.MessageType = ptype.PlaceholderMessage(m.FullName)
252 } else if mv, ok := messageDescCache.Load(t); ok {
253 f.MessageType = mv.(pref.MessageDescriptor)
254 } else {
255 f.MessageType = ms.processMessage(t)
256 }
257 }
258 return f
259}
260
261func (ms *messageDescSet) visit(m *ptype.StandaloneMessage, t reflect.Type) {
262 if ms.visited == nil {
263 ms.visited = make(map[reflect.Type]*ptype.StandaloneMessage)
264 }
265 if t.Kind() != reflect.Map {
266 ms.visited[t] = m
267 }
268 ms.descs = append(ms.descs, m)
269 ms.types = append(ms.types, t)
270}
271
272// deriveFullName derives a fully qualified protobuf name for the given Go type
273// The provided name is not guaranteed to be stable nor universally unique.
274// It should be sufficiently unique within a program.
275func deriveFullName(t reflect.Type) pref.FullName {
276 sanitize := func(r rune) rune {
277 switch {
278 case r == '/':
279 return '.'
280 case 'a' <= r && r <= 'z', 'A' <= r && r <= 'Z', '0' <= r && r <= '9':
281 return r
282 default:
283 return '_'
284 }
285 }
286 prefix := strings.Map(sanitize, t.PkgPath())
287 suffix := strings.Map(sanitize, t.Name())
288 if suffix == "" {
289 suffix = fmt.Sprintf("UnknownX%X", reflect.ValueOf(t).Pointer())
290 }
291
292 ss := append(strings.Split(prefix, "."), suffix)
293 for i, s := range ss {
294 if s == "" || ('0' <= s[0] && s[0] <= '9') {
295 ss[i] = "x" + s
296 }
297 }
298 return pref.FullName(strings.Join(ss, "."))
299}
300
301// mapEntryName derives the message name for a map field of a given name.
302// This is identical to MapEntryName from parser.cc in the protoc source.
303func mapEntryName(s pref.Name) pref.Name {
304 var b []byte
305 nextUpper := true
306 for i := 0; i < len(s); i++ {
307 if c := s[i]; c == '_' {
308 nextUpper = true
309 } else {
310 if nextUpper {
311 c = byte(unicode.ToUpper(rune(c)))
312 nextUpper = false
313 }
314 b = append(b, c)
315 }
316 }
317 return pref.Name(append(b, "Entry"...))
318}