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