blob: 5b4beb0558b668b241e349195c8b91c6fb175925 [file] [log] [blame]
Joe Tsaifa02f4e2018-09-12 16:20:37 -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 (
Joe Tsaic6b75612018-09-13 14:24:37 -07008 "fmt"
Joe Tsaifa02f4e2018-09-12 16:20:37 -07009 "reflect"
10 "strconv"
11 "strings"
Joe Tsaic6b75612018-09-13 14:24:37 -070012 "sync"
Damien Neilc37adef2019-04-01 13:49:56 -070013 "sync/atomic"
Joe Tsaifa02f4e2018-09-12 16:20:37 -070014
Damien Neile89e6242019-05-13 23:55:40 -070015 pref "google.golang.org/protobuf/reflect/protoreflect"
16 piface "google.golang.org/protobuf/runtime/protoiface"
Joe Tsaifa02f4e2018-09-12 16:20:37 -070017)
18
Joe Tsai4fe96632019-05-22 05:12:36 -040019// MessageInfo provides protobuf related functionality for a given Go type
20// that represents a message. A given instance of MessageInfo is tied to
Joe Tsaic6b75612018-09-13 14:24:37 -070021// exactly one Go type, which must be a pointer to a struct type.
Joe Tsai4fe96632019-05-22 05:12:36 -040022type MessageInfo struct {
Damien Neil8012b442019-01-18 09:32:24 -080023 // GoType is the underlying message Go type and must be populated.
Joe Tsaic6b75612018-09-13 14:24:37 -070024 // Once set, this field must never be mutated.
Damien Neil8012b442019-01-18 09:32:24 -080025 GoType reflect.Type // pointer to struct
26
27 // PBType is the underlying message descriptor type and must be populated.
28 // Once set, this field must never be mutated.
29 PBType pref.MessageType
Joe Tsaic6b75612018-09-13 14:24:37 -070030
Joe Tsaic0e4bb22019-07-06 13:05:11 -070031 // Exporter must be provided in a purego environment in order to provide
32 // access to unexported fields.
33 Exporter exporter
34
Joe Tsai09912272019-07-08 10:38:11 -070035 // OneofWrappers is list of pointers to oneof wrapper struct types.
36 OneofWrappers []interface{}
37
Damien Neilc37adef2019-04-01 13:49:56 -070038 initMu sync.Mutex // protects all unexported fields
39 initDone uint32
Joe Tsaic6b75612018-09-13 14:24:37 -070040
Joe Tsai82760ce2019-06-20 03:09:57 -070041 reflectMessageInfo
Joe Tsai378c1322019-04-25 23:48:08 -070042
Damien Neil4ae30bb2019-06-20 10:12:23 -070043 // Information used by the fast-path methods.
Joe Tsai4a539f42019-06-17 12:30:25 -070044 methods piface.Methods
Damien Neil4ae30bb2019-06-20 10:12:23 -070045 coderMessageInfo
Damien Neilc37adef2019-04-01 13:49:56 -070046
Damien Neilc37adef2019-04-01 13:49:56 -070047 extensionFieldInfosMu sync.RWMutex
Joe Tsai89d49632019-06-04 16:20:00 -070048 extensionFieldInfos map[pref.ExtensionType]*extensionFieldInfo
Damien Neilc37adef2019-04-01 13:49:56 -070049}
50
Joe Tsai82760ce2019-06-20 03:09:57 -070051type reflectMessageInfo struct {
52 fields map[pref.FieldNumber]*fieldInfo
53 oneofs map[pref.Name]*oneofInfo
54
55 getUnknown func(pointer) pref.RawFields
56 setUnknown func(pointer, pref.RawFields)
57 extensionMap func(pointer) *extensionMap
58
59 nilMessage atomicNilMessage
60}
61
Joe Tsaic0e4bb22019-07-06 13:05:11 -070062// exporter is a function that returns a reference to the ith field of v,
63// where v is a pointer to a struct. It returns nil if it does not support
64// exporting the requested field (e.g., already exported).
65type exporter func(v interface{}, i int) interface{}
66
Damien Neilc37adef2019-04-01 13:49:56 -070067var prefMessageType = reflect.TypeOf((*pref.Message)(nil)).Elem()
68
Joe Tsai4fe96632019-05-22 05:12:36 -040069// getMessageInfo returns the MessageInfo (if any) for a type.
Damien Neilc37adef2019-04-01 13:49:56 -070070//
Joe Tsai4fe96632019-05-22 05:12:36 -040071// We find the MessageInfo by calling the ProtoReflect method on the type's
Damien Neilc37adef2019-04-01 13:49:56 -070072// zero value and looking at the returned type to see if it is a
Joe Tsai4fe96632019-05-22 05:12:36 -040073// messageReflectWrapper. Note that the MessageInfo may still be uninitialized
Damien Neilc37adef2019-04-01 13:49:56 -070074// at this point.
Joe Tsai4fe96632019-05-22 05:12:36 -040075func getMessageInfo(mt reflect.Type) (mi *MessageInfo, ok bool) {
Damien Neilc37adef2019-04-01 13:49:56 -070076 method, ok := mt.MethodByName("ProtoReflect")
77 if !ok {
78 return nil, false
79 }
80 if method.Type.NumIn() != 1 || method.Type.NumOut() != 1 || method.Type.Out(0) != prefMessageType {
81 return nil, false
82 }
83 ret := reflect.Zero(mt).Method(method.Index).Call(nil)
84 m, ok := ret[0].Elem().Interface().(*messageReflectWrapper)
85 if !ok {
86 return nil, ok
87 }
88 return m.mi, true
Joe Tsaifa02f4e2018-09-12 16:20:37 -070089}
90
Joe Tsai4fe96632019-05-22 05:12:36 -040091func (mi *MessageInfo) init() {
Damien Neilc37adef2019-04-01 13:49:56 -070092 // This function is called in the hot path. Inline the sync.Once
93 // logic, since allocating a closure for Once.Do is expensive.
94 // Keep init small to ensure that it can be inlined.
Joe Tsai82760ce2019-06-20 03:09:57 -070095 if atomic.LoadUint32(&mi.initDone) == 0 {
96 mi.initOnce()
Damien Neilc37adef2019-04-01 13:49:56 -070097 }
Damien Neilc37adef2019-04-01 13:49:56 -070098}
Joe Tsaic6b75612018-09-13 14:24:37 -070099
Joe Tsai4fe96632019-05-22 05:12:36 -0400100func (mi *MessageInfo) initOnce() {
Damien Neilc37adef2019-04-01 13:49:56 -0700101 mi.initMu.Lock()
102 defer mi.initMu.Unlock()
103 if mi.initDone == 1 {
104 return
105 }
106
107 t := mi.GoType
108 if t.Kind() != reflect.Ptr && t.Elem().Kind() != reflect.Struct {
109 panic(fmt.Sprintf("got %v, want *struct kind", t))
110 }
111
112 si := mi.makeStructInfo(t.Elem())
113 mi.makeKnownFieldsFunc(si)
Joe Tsai93fd9682019-07-06 13:02:14 -0700114 mi.makeUnknownFieldsFunc(t.Elem(), si)
115 mi.makeExtensionFieldsFunc(t.Elem(), si)
Damien Neil4ae30bb2019-06-20 10:12:23 -0700116 mi.makeMethods(t.Elem(), si)
Damien Neilc37adef2019-04-01 13:49:56 -0700117
118 atomic.StoreUint32(&mi.initDone, 1)
119}
120
Joe Tsai378c1322019-04-25 23:48:08 -0700121type (
122 SizeCache = int32
Joe Tsai3d8e3692019-04-08 13:52:14 -0700123 WeakFields = map[int32]piface.MessageV1
Joe Tsai378c1322019-04-25 23:48:08 -0700124 UnknownFields = []byte
125 ExtensionFields = map[int32]ExtensionField
126)
127
128var (
129 sizecacheType = reflect.TypeOf(SizeCache(0))
Joe Tsai3d8e3692019-04-08 13:52:14 -0700130 weakFieldsType = reflect.TypeOf(WeakFields(nil))
Joe Tsai378c1322019-04-25 23:48:08 -0700131 unknownFieldsType = reflect.TypeOf(UnknownFields(nil))
132 extensionFieldsType = reflect.TypeOf(ExtensionFields(nil))
133)
Damien Neilc37adef2019-04-01 13:49:56 -0700134
Damien Neil3eaddf02019-05-09 11:33:55 -0700135type structInfo struct {
Joe Tsai93fd9682019-07-06 13:02:14 -0700136 sizecacheOffset offset
Joe Tsai3d8e3692019-04-08 13:52:14 -0700137 weakOffset offset
Joe Tsai93fd9682019-07-06 13:02:14 -0700138 unknownOffset offset
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700139 extensionOffset offset
Joe Tsai93fd9682019-07-06 13:02:14 -0700140
Damien Neil3eaddf02019-05-09 11:33:55 -0700141 fieldsByNumber map[pref.FieldNumber]reflect.StructField
142 oneofsByName map[pref.Name]reflect.StructField
143 oneofWrappersByType map[reflect.Type]pref.FieldNumber
144 oneofWrappersByNumber map[pref.FieldNumber]reflect.Type
145}
146
Joe Tsai4fe96632019-05-22 05:12:36 -0400147func (mi *MessageInfo) makeStructInfo(t reflect.Type) structInfo {
Damien Neil3eaddf02019-05-09 11:33:55 -0700148 si := structInfo{
Joe Tsai93fd9682019-07-06 13:02:14 -0700149 sizecacheOffset: invalidOffset,
Joe Tsai3d8e3692019-04-08 13:52:14 -0700150 weakOffset: invalidOffset,
Joe Tsai93fd9682019-07-06 13:02:14 -0700151 unknownOffset: invalidOffset,
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700152 extensionOffset: invalidOffset,
Joe Tsai93fd9682019-07-06 13:02:14 -0700153
Damien Neil3eaddf02019-05-09 11:33:55 -0700154 fieldsByNumber: map[pref.FieldNumber]reflect.StructField{},
155 oneofsByName: map[pref.Name]reflect.StructField{},
156 oneofWrappersByType: map[reflect.Type]pref.FieldNumber{},
157 oneofWrappersByNumber: map[pref.FieldNumber]reflect.Type{},
158 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700159
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700160 if f, _ := t.FieldByName("sizeCache"); f.Type == sizecacheType {
161 si.sizecacheOffset = offsetOf(f, mi.Exporter)
162 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700163 if f, _ := t.FieldByName("XXX_sizecache"); f.Type == sizecacheType {
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700164 si.sizecacheOffset = offsetOf(f, mi.Exporter)
Joe Tsai93fd9682019-07-06 13:02:14 -0700165 }
Joe Tsai3d8e3692019-04-08 13:52:14 -0700166 if f, _ := t.FieldByName("XXX_weak"); f.Type == weakFieldsType {
167 si.weakOffset = offsetOf(f, mi.Exporter)
168 }
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700169 if f, _ := t.FieldByName("unknownFields"); f.Type == unknownFieldsType {
170 si.unknownOffset = offsetOf(f, mi.Exporter)
Joe Tsai93fd9682019-07-06 13:02:14 -0700171 }
172 if f, _ := t.FieldByName("XXX_unrecognized"); f.Type == unknownFieldsType {
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700173 si.unknownOffset = offsetOf(f, mi.Exporter)
174 }
175 if f, _ := t.FieldByName("extensionFields"); f.Type == extensionFieldsType {
176 si.extensionOffset = offsetOf(f, mi.Exporter)
177 }
178 if f, _ := t.FieldByName("XXX_InternalExtensions"); f.Type == extensionFieldsType {
179 si.extensionOffset = offsetOf(f, mi.Exporter)
180 }
181 if f, _ := t.FieldByName("XXX_extensions"); f.Type == extensionFieldsType {
182 si.extensionOffset = offsetOf(f, mi.Exporter)
Joe Tsai93fd9682019-07-06 13:02:14 -0700183 }
184
185 // Generate a mapping of field numbers and names to Go struct field or type.
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700186fieldLoop:
187 for i := 0; i < t.NumField(); i++ {
188 f := t.Field(i)
189 for _, s := range strings.Split(f.Tag.Get("protobuf"), ",") {
190 if len(s) > 0 && strings.Trim(s, "0123456789") == "" {
191 n, _ := strconv.ParseUint(s, 10, 64)
Damien Neil3eaddf02019-05-09 11:33:55 -0700192 si.fieldsByNumber[pref.FieldNumber(n)] = f
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700193 continue fieldLoop
194 }
195 }
196 if s := f.Tag.Get("protobuf_oneof"); len(s) > 0 {
Damien Neil3eaddf02019-05-09 11:33:55 -0700197 si.oneofsByName[pref.Name(s)] = f
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700198 continue fieldLoop
199 }
200 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700201
202 // Derive a mapping of oneof wrappers to fields.
Joe Tsai09912272019-07-08 10:38:11 -0700203 oneofWrappers := mi.OneofWrappers
Joe Tsai2c870bb2018-10-17 11:46:52 -0700204 if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofFuncs"); ok {
Joe Tsaid7e97bc2018-11-26 12:57:27 -0800205 oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[3].Interface().([]interface{})
206 }
207 if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofWrappers"); ok {
208 oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0].Interface().([]interface{})
209 }
210 for _, v := range oneofWrappers {
211 tf := reflect.TypeOf(v).Elem()
212 f := tf.Field(0)
213 for _, s := range strings.Split(f.Tag.Get("protobuf"), ",") {
214 if len(s) > 0 && strings.Trim(s, "0123456789") == "" {
215 n, _ := strconv.ParseUint(s, 10, 64)
Damien Neil3eaddf02019-05-09 11:33:55 -0700216 si.oneofWrappersByType[tf] = pref.FieldNumber(n)
217 si.oneofWrappersByNumber[pref.FieldNumber(n)] = tf
Joe Tsaid7e97bc2018-11-26 12:57:27 -0800218 break
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700219 }
220 }
221 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700222
Damien Neil3eaddf02019-05-09 11:33:55 -0700223 return si
224}
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700225
Damien Neil3eaddf02019-05-09 11:33:55 -0700226// makeKnownFieldsFunc generates functions for operations that can be performed
227// on each protobuf message field. It takes in a reflect.Type representing the
228// Go struct and matches message fields with struct fields.
229//
230// This code assumes that the struct is well-formed and panics if there are
231// any discrepancies.
Joe Tsai4fe96632019-05-22 05:12:36 -0400232func (mi *MessageInfo) makeKnownFieldsFunc(si structInfo) {
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700233 mi.fields = map[pref.FieldNumber]*fieldInfo{}
Joe Tsaid4211502019-07-02 14:58:02 -0700234 for i := 0; i < mi.PBType.Fields().Len(); i++ {
235 fd := mi.PBType.Fields().Get(i)
Damien Neil3eaddf02019-05-09 11:33:55 -0700236 fs := si.fieldsByNumber[fd.Number()]
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700237 var fi fieldInfo
238 switch {
Joe Tsaiac31a352019-05-13 14:32:56 -0700239 case fd.ContainingOneof() != nil:
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700240 fi = fieldInfoForOneof(fd, si.oneofsByName[fd.ContainingOneof().Name()], mi.Exporter, si.oneofWrappersByNumber[fd.Number()])
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700241 case fd.IsMap():
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700242 fi = fieldInfoForMap(fd, fs, mi.Exporter)
Joe Tsaiac31a352019-05-13 14:32:56 -0700243 case fd.IsList():
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700244 fi = fieldInfoForList(fd, fs, mi.Exporter)
Joe Tsai3d8e3692019-04-08 13:52:14 -0700245 case fd.IsWeak():
246 fi = fieldInfoForWeakMessage(fd, si.weakOffset)
Joe Tsaic6b75612018-09-13 14:24:37 -0700247 case fd.Kind() == pref.MessageKind || fd.Kind() == pref.GroupKind:
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700248 fi = fieldInfoForMessage(fd, fs, mi.Exporter)
Joe Tsaic6b75612018-09-13 14:24:37 -0700249 default:
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700250 fi = fieldInfoForScalar(fd, fs, mi.Exporter)
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700251 }
252 mi.fields[fd.Number()] = &fi
253 }
Joe Tsai4ec39c72019-04-03 13:40:53 -0700254
255 mi.oneofs = map[pref.Name]*oneofInfo{}
Joe Tsaid4211502019-07-02 14:58:02 -0700256 for i := 0; i < mi.PBType.Oneofs().Len(); i++ {
257 od := mi.PBType.Oneofs().Get(i)
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700258 mi.oneofs[od.Name()] = makeOneofInfo(od, si.oneofsByName[od.Name()], mi.Exporter, si.oneofWrappersByType)
Joe Tsai4ec39c72019-04-03 13:40:53 -0700259 }
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700260}
Joe Tsaic6b75612018-09-13 14:24:37 -0700261
Joe Tsai93fd9682019-07-06 13:02:14 -0700262func (mi *MessageInfo) makeUnknownFieldsFunc(t reflect.Type, si structInfo) {
Joe Tsai378c1322019-04-25 23:48:08 -0700263 mi.getUnknown = func(pointer) pref.RawFields { return nil }
264 mi.setUnknown = func(pointer, pref.RawFields) { return }
Joe Tsai93fd9682019-07-06 13:02:14 -0700265 if si.unknownOffset.IsValid() {
Joe Tsai378c1322019-04-25 23:48:08 -0700266 mi.getUnknown = func(p pointer) pref.RawFields {
267 if p.IsNil() {
268 return nil
269 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700270 rv := p.Apply(si.unknownOffset).AsValueOf(unknownFieldsType)
Joe Tsai378c1322019-04-25 23:48:08 -0700271 return pref.RawFields(*rv.Interface().(*[]byte))
272 }
273 mi.setUnknown = func(p pointer, b pref.RawFields) {
274 if p.IsNil() {
275 panic("invalid SetUnknown on nil Message")
276 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700277 rv := p.Apply(si.unknownOffset).AsValueOf(unknownFieldsType)
Joe Tsai378c1322019-04-25 23:48:08 -0700278 *rv.Interface().(*[]byte) = []byte(b)
279 }
280 } else {
281 mi.getUnknown = func(pointer) pref.RawFields {
282 return nil
283 }
284 mi.setUnknown = func(p pointer, _ pref.RawFields) {
285 if p.IsNil() {
286 panic("invalid SetUnknown on nil Message")
287 }
288 }
Joe Tsaibe5348c2018-10-23 18:31:18 -0700289 }
290}
291
Joe Tsai93fd9682019-07-06 13:02:14 -0700292func (mi *MessageInfo) makeExtensionFieldsFunc(t reflect.Type, si structInfo) {
293 if si.extensionOffset.IsValid() {
Joe Tsai378c1322019-04-25 23:48:08 -0700294 mi.extensionMap = func(p pointer) *extensionMap {
Joe Tsaid8881392019-06-06 13:01:53 -0700295 if p.IsNil() {
296 return (*extensionMap)(nil)
297 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700298 v := p.Apply(si.extensionOffset).AsValueOf(extensionFieldsType)
Joe Tsai378c1322019-04-25 23:48:08 -0700299 return (*extensionMap)(v.Interface().(*map[int32]ExtensionField))
300 }
301 } else {
302 mi.extensionMap = func(pointer) *extensionMap {
303 return (*extensionMap)(nil)
304 }
Joe Tsaibe5348c2018-10-23 18:31:18 -0700305 }
306}