blob: 9e1c3a3364fbaf0a031d4db26a115b528391c489 [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 Neil16163b42019-08-06 15:43:25 -070015 "google.golang.org/protobuf/reflect/protoreflect"
Damien Neile89e6242019-05-13 23:55:40 -070016 pref "google.golang.org/protobuf/reflect/protoreflect"
17 piface "google.golang.org/protobuf/runtime/protoiface"
Joe Tsaifa02f4e2018-09-12 16:20:37 -070018)
19
Joe Tsai4fe96632019-05-22 05:12:36 -040020// MessageInfo provides protobuf related functionality for a given Go type
21// that represents a message. A given instance of MessageInfo is tied to
Joe Tsaic6b75612018-09-13 14:24:37 -070022// exactly one Go type, which must be a pointer to a struct type.
Joe Tsai4fe96632019-05-22 05:12:36 -040023type MessageInfo struct {
Damien Neil16163b42019-08-06 15:43:25 -070024 // GoReflectType is the underlying message Go type and must be populated.
Joe Tsaic6b75612018-09-13 14:24:37 -070025 // Once set, this field must never be mutated.
Damien Neil16163b42019-08-06 15:43:25 -070026 GoReflectType reflect.Type // pointer to struct
Damien Neil8012b442019-01-18 09:32:24 -080027
Damien Neil16163b42019-08-06 15:43:25 -070028 // Desc is the underlying message descriptor type and must be populated.
Damien Neil8012b442019-01-18 09:32:24 -080029 // Once set, this field must never be mutated.
Damien Neil16163b42019-08-06 15:43:25 -070030 Desc pref.MessageDescriptor
Joe Tsaic6b75612018-09-13 14:24:37 -070031
Joe Tsaic0e4bb22019-07-06 13:05:11 -070032 // Exporter must be provided in a purego environment in order to provide
33 // access to unexported fields.
34 Exporter exporter
35
Joe Tsai09912272019-07-08 10:38:11 -070036 // OneofWrappers is list of pointers to oneof wrapper struct types.
37 OneofWrappers []interface{}
38
Damien Neilc37adef2019-04-01 13:49:56 -070039 initMu sync.Mutex // protects all unexported fields
40 initDone uint32
Joe Tsaic6b75612018-09-13 14:24:37 -070041
Joe Tsai82760ce2019-06-20 03:09:57 -070042 reflectMessageInfo
Joe Tsai378c1322019-04-25 23:48:08 -070043
Damien Neil4ae30bb2019-06-20 10:12:23 -070044 // Information used by the fast-path methods.
Joe Tsai4a539f42019-06-17 12:30:25 -070045 methods piface.Methods
Damien Neil4ae30bb2019-06-20 10:12:23 -070046 coderMessageInfo
Damien Neilc37adef2019-04-01 13:49:56 -070047
Damien Neilc37adef2019-04-01 13:49:56 -070048 extensionFieldInfosMu sync.RWMutex
Joe Tsai89d49632019-06-04 16:20:00 -070049 extensionFieldInfos map[pref.ExtensionType]*extensionFieldInfo
Damien Neilc37adef2019-04-01 13:49:56 -070050}
51
Joe Tsai82760ce2019-06-20 03:09:57 -070052type reflectMessageInfo struct {
53 fields map[pref.FieldNumber]*fieldInfo
54 oneofs map[pref.Name]*oneofInfo
55
56 getUnknown func(pointer) pref.RawFields
57 setUnknown func(pointer, pref.RawFields)
58 extensionMap func(pointer) *extensionMap
59
60 nilMessage atomicNilMessage
61}
62
Joe Tsaic0e4bb22019-07-06 13:05:11 -070063// exporter is a function that returns a reference to the ith field of v,
64// where v is a pointer to a struct. It returns nil if it does not support
65// exporting the requested field (e.g., already exported).
66type exporter func(v interface{}, i int) interface{}
67
Joe Tsai070c1012019-07-26 23:45:58 -070068// getMessageInfo returns the MessageInfo for any message type that
69// is generated by our implementation of protoc-gen-go (for v2 and on).
70// If it is unable to obtain a MessageInfo, it returns nil.
71func getMessageInfo(mt reflect.Type) *MessageInfo {
72 m, ok := reflect.Zero(mt).Interface().(pref.ProtoMessage)
Damien Neilc37adef2019-04-01 13:49:56 -070073 if !ok {
Joe Tsai070c1012019-07-26 23:45:58 -070074 return nil
Damien Neilc37adef2019-04-01 13:49:56 -070075 }
Joe Tsai070c1012019-07-26 23:45:58 -070076 mr, ok := m.ProtoReflect().(interface{ ProtoMessageInfo() *MessageInfo })
Damien Neilc37adef2019-04-01 13:49:56 -070077 if !ok {
Joe Tsai070c1012019-07-26 23:45:58 -070078 return nil
Damien Neilc37adef2019-04-01 13:49:56 -070079 }
Joe Tsai070c1012019-07-26 23:45:58 -070080 return mr.ProtoMessageInfo()
Joe Tsaifa02f4e2018-09-12 16:20:37 -070081}
82
Joe Tsai4fe96632019-05-22 05:12:36 -040083func (mi *MessageInfo) init() {
Damien Neilc37adef2019-04-01 13:49:56 -070084 // This function is called in the hot path. Inline the sync.Once
85 // logic, since allocating a closure for Once.Do is expensive.
86 // Keep init small to ensure that it can be inlined.
Joe Tsai82760ce2019-06-20 03:09:57 -070087 if atomic.LoadUint32(&mi.initDone) == 0 {
88 mi.initOnce()
Damien Neilc37adef2019-04-01 13:49:56 -070089 }
Damien Neilc37adef2019-04-01 13:49:56 -070090}
Joe Tsaic6b75612018-09-13 14:24:37 -070091
Joe Tsai4fe96632019-05-22 05:12:36 -040092func (mi *MessageInfo) initOnce() {
Damien Neilc37adef2019-04-01 13:49:56 -070093 mi.initMu.Lock()
94 defer mi.initMu.Unlock()
95 if mi.initDone == 1 {
96 return
97 }
98
Damien Neil16163b42019-08-06 15:43:25 -070099 t := mi.GoReflectType
Damien Neilc37adef2019-04-01 13:49:56 -0700100 if t.Kind() != reflect.Ptr && t.Elem().Kind() != reflect.Struct {
101 panic(fmt.Sprintf("got %v, want *struct kind", t))
102 }
103
104 si := mi.makeStructInfo(t.Elem())
105 mi.makeKnownFieldsFunc(si)
Joe Tsai93fd9682019-07-06 13:02:14 -0700106 mi.makeUnknownFieldsFunc(t.Elem(), si)
107 mi.makeExtensionFieldsFunc(t.Elem(), si)
Damien Neil4ae30bb2019-06-20 10:12:23 -0700108 mi.makeMethods(t.Elem(), si)
Damien Neilc37adef2019-04-01 13:49:56 -0700109
110 atomic.StoreUint32(&mi.initDone, 1)
111}
112
Joe Tsai378c1322019-04-25 23:48:08 -0700113type (
114 SizeCache = int32
Joe Tsai3d8e3692019-04-08 13:52:14 -0700115 WeakFields = map[int32]piface.MessageV1
Joe Tsai378c1322019-04-25 23:48:08 -0700116 UnknownFields = []byte
117 ExtensionFields = map[int32]ExtensionField
118)
119
120var (
121 sizecacheType = reflect.TypeOf(SizeCache(0))
Joe Tsai3d8e3692019-04-08 13:52:14 -0700122 weakFieldsType = reflect.TypeOf(WeakFields(nil))
Joe Tsai378c1322019-04-25 23:48:08 -0700123 unknownFieldsType = reflect.TypeOf(UnknownFields(nil))
124 extensionFieldsType = reflect.TypeOf(ExtensionFields(nil))
125)
Damien Neilc37adef2019-04-01 13:49:56 -0700126
Damien Neil3eaddf02019-05-09 11:33:55 -0700127type structInfo struct {
Joe Tsai93fd9682019-07-06 13:02:14 -0700128 sizecacheOffset offset
Joe Tsai3d8e3692019-04-08 13:52:14 -0700129 weakOffset offset
Joe Tsai93fd9682019-07-06 13:02:14 -0700130 unknownOffset offset
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700131 extensionOffset offset
Joe Tsai93fd9682019-07-06 13:02:14 -0700132
Damien Neil3eaddf02019-05-09 11:33:55 -0700133 fieldsByNumber map[pref.FieldNumber]reflect.StructField
134 oneofsByName map[pref.Name]reflect.StructField
135 oneofWrappersByType map[reflect.Type]pref.FieldNumber
136 oneofWrappersByNumber map[pref.FieldNumber]reflect.Type
137}
138
Joe Tsai4fe96632019-05-22 05:12:36 -0400139func (mi *MessageInfo) makeStructInfo(t reflect.Type) structInfo {
Damien Neil3eaddf02019-05-09 11:33:55 -0700140 si := structInfo{
Joe Tsai93fd9682019-07-06 13:02:14 -0700141 sizecacheOffset: invalidOffset,
Joe Tsai3d8e3692019-04-08 13:52:14 -0700142 weakOffset: invalidOffset,
Joe Tsai93fd9682019-07-06 13:02:14 -0700143 unknownOffset: invalidOffset,
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700144 extensionOffset: invalidOffset,
Joe Tsai93fd9682019-07-06 13:02:14 -0700145
Damien Neil3eaddf02019-05-09 11:33:55 -0700146 fieldsByNumber: map[pref.FieldNumber]reflect.StructField{},
147 oneofsByName: map[pref.Name]reflect.StructField{},
148 oneofWrappersByType: map[reflect.Type]pref.FieldNumber{},
149 oneofWrappersByNumber: map[pref.FieldNumber]reflect.Type{},
150 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700151
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700152 if f, _ := t.FieldByName("sizeCache"); f.Type == sizecacheType {
153 si.sizecacheOffset = offsetOf(f, mi.Exporter)
154 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700155 if f, _ := t.FieldByName("XXX_sizecache"); f.Type == sizecacheType {
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700156 si.sizecacheOffset = offsetOf(f, mi.Exporter)
Joe Tsai93fd9682019-07-06 13:02:14 -0700157 }
Joe Tsai3d8e3692019-04-08 13:52:14 -0700158 if f, _ := t.FieldByName("XXX_weak"); f.Type == weakFieldsType {
159 si.weakOffset = offsetOf(f, mi.Exporter)
160 }
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700161 if f, _ := t.FieldByName("unknownFields"); f.Type == unknownFieldsType {
162 si.unknownOffset = offsetOf(f, mi.Exporter)
Joe Tsai93fd9682019-07-06 13:02:14 -0700163 }
164 if f, _ := t.FieldByName("XXX_unrecognized"); f.Type == unknownFieldsType {
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700165 si.unknownOffset = offsetOf(f, mi.Exporter)
166 }
167 if f, _ := t.FieldByName("extensionFields"); f.Type == extensionFieldsType {
168 si.extensionOffset = offsetOf(f, mi.Exporter)
169 }
170 if f, _ := t.FieldByName("XXX_InternalExtensions"); f.Type == extensionFieldsType {
171 si.extensionOffset = offsetOf(f, mi.Exporter)
172 }
173 if f, _ := t.FieldByName("XXX_extensions"); f.Type == extensionFieldsType {
174 si.extensionOffset = offsetOf(f, mi.Exporter)
Joe Tsai93fd9682019-07-06 13:02:14 -0700175 }
176
177 // Generate a mapping of field numbers and names to Go struct field or type.
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700178fieldLoop:
179 for i := 0; i < t.NumField(); i++ {
180 f := t.Field(i)
181 for _, s := range strings.Split(f.Tag.Get("protobuf"), ",") {
182 if len(s) > 0 && strings.Trim(s, "0123456789") == "" {
183 n, _ := strconv.ParseUint(s, 10, 64)
Damien Neil3eaddf02019-05-09 11:33:55 -0700184 si.fieldsByNumber[pref.FieldNumber(n)] = f
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700185 continue fieldLoop
186 }
187 }
188 if s := f.Tag.Get("protobuf_oneof"); len(s) > 0 {
Damien Neil3eaddf02019-05-09 11:33:55 -0700189 si.oneofsByName[pref.Name(s)] = f
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700190 continue fieldLoop
191 }
192 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700193
194 // Derive a mapping of oneof wrappers to fields.
Joe Tsai09912272019-07-08 10:38:11 -0700195 oneofWrappers := mi.OneofWrappers
Joe Tsai2c870bb2018-10-17 11:46:52 -0700196 if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofFuncs"); ok {
Joe Tsaid7e97bc2018-11-26 12:57:27 -0800197 oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[3].Interface().([]interface{})
198 }
199 if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofWrappers"); ok {
200 oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0].Interface().([]interface{})
201 }
202 for _, v := range oneofWrappers {
203 tf := reflect.TypeOf(v).Elem()
204 f := tf.Field(0)
205 for _, s := range strings.Split(f.Tag.Get("protobuf"), ",") {
206 if len(s) > 0 && strings.Trim(s, "0123456789") == "" {
207 n, _ := strconv.ParseUint(s, 10, 64)
Damien Neil3eaddf02019-05-09 11:33:55 -0700208 si.oneofWrappersByType[tf] = pref.FieldNumber(n)
209 si.oneofWrappersByNumber[pref.FieldNumber(n)] = tf
Joe Tsaid7e97bc2018-11-26 12:57:27 -0800210 break
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700211 }
212 }
213 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700214
Damien Neil3eaddf02019-05-09 11:33:55 -0700215 return si
216}
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700217
Damien Neil3eaddf02019-05-09 11:33:55 -0700218// makeKnownFieldsFunc generates functions for operations that can be performed
219// on each protobuf message field. It takes in a reflect.Type representing the
220// Go struct and matches message fields with struct fields.
221//
222// This code assumes that the struct is well-formed and panics if there are
223// any discrepancies.
Joe Tsai4fe96632019-05-22 05:12:36 -0400224func (mi *MessageInfo) makeKnownFieldsFunc(si structInfo) {
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700225 mi.fields = map[pref.FieldNumber]*fieldInfo{}
Damien Neil16163b42019-08-06 15:43:25 -0700226 md := mi.Desc
Damien Neil92f76182019-08-02 16:58:08 -0700227 for i := 0; i < md.Fields().Len(); i++ {
228 fd := md.Fields().Get(i)
Damien Neil3eaddf02019-05-09 11:33:55 -0700229 fs := si.fieldsByNumber[fd.Number()]
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700230 var fi fieldInfo
231 switch {
Joe Tsaiac31a352019-05-13 14:32:56 -0700232 case fd.ContainingOneof() != nil:
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700233 fi = fieldInfoForOneof(fd, si.oneofsByName[fd.ContainingOneof().Name()], mi.Exporter, si.oneofWrappersByNumber[fd.Number()])
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700234 case fd.IsMap():
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700235 fi = fieldInfoForMap(fd, fs, mi.Exporter)
Joe Tsaiac31a352019-05-13 14:32:56 -0700236 case fd.IsList():
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700237 fi = fieldInfoForList(fd, fs, mi.Exporter)
Joe Tsai3d8e3692019-04-08 13:52:14 -0700238 case fd.IsWeak():
239 fi = fieldInfoForWeakMessage(fd, si.weakOffset)
Joe Tsaic6b75612018-09-13 14:24:37 -0700240 case fd.Kind() == pref.MessageKind || fd.Kind() == pref.GroupKind:
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700241 fi = fieldInfoForMessage(fd, fs, mi.Exporter)
Joe Tsaic6b75612018-09-13 14:24:37 -0700242 default:
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700243 fi = fieldInfoForScalar(fd, fs, mi.Exporter)
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700244 }
245 mi.fields[fd.Number()] = &fi
246 }
Joe Tsai4ec39c72019-04-03 13:40:53 -0700247
248 mi.oneofs = map[pref.Name]*oneofInfo{}
Damien Neil92f76182019-08-02 16:58:08 -0700249 for i := 0; i < md.Oneofs().Len(); i++ {
250 od := md.Oneofs().Get(i)
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700251 mi.oneofs[od.Name()] = makeOneofInfo(od, si.oneofsByName[od.Name()], mi.Exporter, si.oneofWrappersByType)
Joe Tsai4ec39c72019-04-03 13:40:53 -0700252 }
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700253}
Joe Tsaic6b75612018-09-13 14:24:37 -0700254
Joe Tsai93fd9682019-07-06 13:02:14 -0700255func (mi *MessageInfo) makeUnknownFieldsFunc(t reflect.Type, si structInfo) {
Joe Tsai378c1322019-04-25 23:48:08 -0700256 mi.getUnknown = func(pointer) pref.RawFields { return nil }
257 mi.setUnknown = func(pointer, pref.RawFields) { return }
Joe Tsai93fd9682019-07-06 13:02:14 -0700258 if si.unknownOffset.IsValid() {
Joe Tsai378c1322019-04-25 23:48:08 -0700259 mi.getUnknown = func(p pointer) pref.RawFields {
260 if p.IsNil() {
261 return nil
262 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700263 rv := p.Apply(si.unknownOffset).AsValueOf(unknownFieldsType)
Joe Tsai378c1322019-04-25 23:48:08 -0700264 return pref.RawFields(*rv.Interface().(*[]byte))
265 }
266 mi.setUnknown = func(p pointer, b pref.RawFields) {
267 if p.IsNil() {
268 panic("invalid SetUnknown on nil Message")
269 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700270 rv := p.Apply(si.unknownOffset).AsValueOf(unknownFieldsType)
Joe Tsai378c1322019-04-25 23:48:08 -0700271 *rv.Interface().(*[]byte) = []byte(b)
272 }
273 } else {
274 mi.getUnknown = func(pointer) pref.RawFields {
275 return nil
276 }
277 mi.setUnknown = func(p pointer, _ pref.RawFields) {
278 if p.IsNil() {
279 panic("invalid SetUnknown on nil Message")
280 }
281 }
Joe Tsaibe5348c2018-10-23 18:31:18 -0700282 }
283}
284
Joe Tsai93fd9682019-07-06 13:02:14 -0700285func (mi *MessageInfo) makeExtensionFieldsFunc(t reflect.Type, si structInfo) {
286 if si.extensionOffset.IsValid() {
Joe Tsai378c1322019-04-25 23:48:08 -0700287 mi.extensionMap = func(p pointer) *extensionMap {
Joe Tsaid8881392019-06-06 13:01:53 -0700288 if p.IsNil() {
289 return (*extensionMap)(nil)
290 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700291 v := p.Apply(si.extensionOffset).AsValueOf(extensionFieldsType)
Joe Tsai378c1322019-04-25 23:48:08 -0700292 return (*extensionMap)(v.Interface().(*map[int32]ExtensionField))
293 }
294 } else {
295 mi.extensionMap = func(pointer) *extensionMap {
296 return (*extensionMap)(nil)
297 }
Joe Tsaibe5348c2018-10-23 18:31:18 -0700298 }
299}
Damien Neil16163b42019-08-06 15:43:25 -0700300
301func (mi *MessageInfo) GoType() reflect.Type {
302 return mi.GoReflectType
303}
304func (mi *MessageInfo) New() protoreflect.Message {
305 return mi.MessageOf(reflect.New(mi.GoReflectType.Elem()).Interface())
306}
307func (mi *MessageInfo) Zero() protoreflect.Message {
308 return mi.MessageOf(reflect.Zero(mi.GoReflectType).Interface())
309}
310func (mi *MessageInfo) Descriptor() protoreflect.MessageDescriptor { return mi.Desc }