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