blob: c026a98180d8d4abebf3800f0238b4ac47f71c59 [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
Joe Tsaie0b77db2020-05-26 11:21:59 -070015 "google.golang.org/protobuf/internal/genid"
Damien Neil16163b42019-08-06 15:43:25 -070016 "google.golang.org/protobuf/reflect/protoreflect"
Damien Neile89e6242019-05-13 23:55:40 -070017 pref "google.golang.org/protobuf/reflect/protoreflect"
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 Tsai0484b1a2019-08-13 15:36:08 -070023//
24// The exported fields must be populated before any methods are called
25// and cannot be mutated after set.
Joe Tsai4fe96632019-05-22 05:12:36 -040026type MessageInfo struct {
Damien Neil16163b42019-08-06 15:43:25 -070027 // GoReflectType is the underlying message Go type and must be populated.
Damien Neil16163b42019-08-06 15:43:25 -070028 GoReflectType reflect.Type // pointer to struct
Damien Neil8012b442019-01-18 09:32:24 -080029
Damien Neil16163b42019-08-06 15:43:25 -070030 // Desc is the underlying message descriptor type and must be populated.
Damien Neil16163b42019-08-06 15:43:25 -070031 Desc pref.MessageDescriptor
Joe Tsaic6b75612018-09-13 14:24:37 -070032
Joe Tsaic0e4bb22019-07-06 13:05:11 -070033 // Exporter must be provided in a purego environment in order to provide
34 // access to unexported fields.
35 Exporter exporter
36
Joe Tsai09912272019-07-08 10:38:11 -070037 // OneofWrappers is list of pointers to oneof wrapper struct types.
38 OneofWrappers []interface{}
39
Damien Neilc37adef2019-04-01 13:49:56 -070040 initMu sync.Mutex // protects all unexported fields
41 initDone uint32
Joe Tsaic6b75612018-09-13 14:24:37 -070042
Joe Tsai0484b1a2019-08-13 15:36:08 -070043 reflectMessageInfo // for reflection implementation
44 coderMessageInfo // for fast-path method implementations
Joe Tsai82760ce2019-06-20 03:09:57 -070045}
46
Joe Tsaic0e4bb22019-07-06 13:05:11 -070047// exporter is a function that returns a reference to the ith field of v,
48// where v is a pointer to a struct. It returns nil if it does not support
49// exporting the requested field (e.g., already exported).
50type exporter func(v interface{}, i int) interface{}
51
Joe Tsai070c1012019-07-26 23:45:58 -070052// getMessageInfo returns the MessageInfo for any message type that
53// is generated by our implementation of protoc-gen-go (for v2 and on).
54// If it is unable to obtain a MessageInfo, it returns nil.
55func getMessageInfo(mt reflect.Type) *MessageInfo {
56 m, ok := reflect.Zero(mt).Interface().(pref.ProtoMessage)
Damien Neilc37adef2019-04-01 13:49:56 -070057 if !ok {
Joe Tsai070c1012019-07-26 23:45:58 -070058 return nil
Damien Neilc37adef2019-04-01 13:49:56 -070059 }
Joe Tsai070c1012019-07-26 23:45:58 -070060 mr, ok := m.ProtoReflect().(interface{ ProtoMessageInfo() *MessageInfo })
Damien Neilc37adef2019-04-01 13:49:56 -070061 if !ok {
Joe Tsai070c1012019-07-26 23:45:58 -070062 return nil
Damien Neilc37adef2019-04-01 13:49:56 -070063 }
Joe Tsai070c1012019-07-26 23:45:58 -070064 return mr.ProtoMessageInfo()
Joe Tsaifa02f4e2018-09-12 16:20:37 -070065}
66
Joe Tsai4fe96632019-05-22 05:12:36 -040067func (mi *MessageInfo) init() {
Joe Tsai0484b1a2019-08-13 15:36:08 -070068 // This function is called in the hot path. Inline the sync.Once logic,
69 // since allocating a closure for Once.Do is expensive.
Damien Neilc37adef2019-04-01 13:49:56 -070070 // Keep init small to ensure that it can be inlined.
Joe Tsai82760ce2019-06-20 03:09:57 -070071 if atomic.LoadUint32(&mi.initDone) == 0 {
72 mi.initOnce()
Damien Neilc37adef2019-04-01 13:49:56 -070073 }
Damien Neilc37adef2019-04-01 13:49:56 -070074}
Joe Tsaic6b75612018-09-13 14:24:37 -070075
Joe Tsai4fe96632019-05-22 05:12:36 -040076func (mi *MessageInfo) initOnce() {
Damien Neilc37adef2019-04-01 13:49:56 -070077 mi.initMu.Lock()
78 defer mi.initMu.Unlock()
79 if mi.initDone == 1 {
80 return
81 }
82
Damien Neil16163b42019-08-06 15:43:25 -070083 t := mi.GoReflectType
Damien Neilc37adef2019-04-01 13:49:56 -070084 if t.Kind() != reflect.Ptr && t.Elem().Kind() != reflect.Struct {
85 panic(fmt.Sprintf("got %v, want *struct kind", t))
86 }
Joe Tsai0484b1a2019-08-13 15:36:08 -070087 t = t.Elem()
Damien Neilc37adef2019-04-01 13:49:56 -070088
Joe Tsai0484b1a2019-08-13 15:36:08 -070089 si := mi.makeStructInfo(t)
90 mi.makeReflectFuncs(t, si)
91 mi.makeCoderMethods(t, si)
Damien Neilc37adef2019-04-01 13:49:56 -070092
93 atomic.StoreUint32(&mi.initDone, 1)
94}
95
Damien Neile8e88752020-02-11 11:25:16 -080096// getPointer returns the pointer for a message, which should be of
97// the type of the MessageInfo. If the message is of a different type,
98// it returns ok==false.
99func (mi *MessageInfo) getPointer(m pref.Message) (p pointer, ok bool) {
100 switch m := m.(type) {
101 case *messageState:
Damien Neil075e0742020-02-27 12:33:08 -0800102 return m.pointer(), m.messageInfo() == mi
Damien Neile8e88752020-02-11 11:25:16 -0800103 case *messageReflectWrapper:
Damien Neil075e0742020-02-27 12:33:08 -0800104 return m.pointer(), m.messageInfo() == mi
Damien Neile8e88752020-02-11 11:25:16 -0800105 }
106 return pointer{}, false
107}
108
Joe Tsai378c1322019-04-25 23:48:08 -0700109type (
110 SizeCache = int32
Joe Tsai3ebaa922020-04-23 00:57:01 -0700111 WeakFields = map[int32]protoreflect.ProtoMessage
Joe Tsai378c1322019-04-25 23:48:08 -0700112 UnknownFields = []byte
113 ExtensionFields = map[int32]ExtensionField
114)
115
116var (
117 sizecacheType = reflect.TypeOf(SizeCache(0))
Joe Tsai3d8e3692019-04-08 13:52:14 -0700118 weakFieldsType = reflect.TypeOf(WeakFields(nil))
Joe Tsai378c1322019-04-25 23:48:08 -0700119 unknownFieldsType = reflect.TypeOf(UnknownFields(nil))
120 extensionFieldsType = reflect.TypeOf(ExtensionFields(nil))
121)
Damien Neilc37adef2019-04-01 13:49:56 -0700122
Damien Neil3eaddf02019-05-09 11:33:55 -0700123type structInfo struct {
Joe Tsai93fd9682019-07-06 13:02:14 -0700124 sizecacheOffset offset
Joe Tsai3d8e3692019-04-08 13:52:14 -0700125 weakOffset offset
Joe Tsai93fd9682019-07-06 13:02:14 -0700126 unknownOffset offset
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700127 extensionOffset offset
Joe Tsai93fd9682019-07-06 13:02:14 -0700128
Damien Neil3eaddf02019-05-09 11:33:55 -0700129 fieldsByNumber map[pref.FieldNumber]reflect.StructField
130 oneofsByName map[pref.Name]reflect.StructField
131 oneofWrappersByType map[reflect.Type]pref.FieldNumber
132 oneofWrappersByNumber map[pref.FieldNumber]reflect.Type
133}
134
Joe Tsai4fe96632019-05-22 05:12:36 -0400135func (mi *MessageInfo) makeStructInfo(t reflect.Type) structInfo {
Damien Neil3eaddf02019-05-09 11:33:55 -0700136 si := structInfo{
Joe Tsai93fd9682019-07-06 13:02:14 -0700137 sizecacheOffset: invalidOffset,
Joe Tsai3d8e3692019-04-08 13:52:14 -0700138 weakOffset: invalidOffset,
Joe Tsai93fd9682019-07-06 13:02:14 -0700139 unknownOffset: invalidOffset,
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700140 extensionOffset: invalidOffset,
Joe Tsai93fd9682019-07-06 13:02:14 -0700141
Damien Neil3eaddf02019-05-09 11:33:55 -0700142 fieldsByNumber: map[pref.FieldNumber]reflect.StructField{},
143 oneofsByName: map[pref.Name]reflect.StructField{},
144 oneofWrappersByType: map[reflect.Type]pref.FieldNumber{},
145 oneofWrappersByNumber: map[pref.FieldNumber]reflect.Type{},
146 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700147
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700148fieldLoop:
149 for i := 0; i < t.NumField(); i++ {
Joe Tsai613285c2019-11-13 01:51:27 -0800150 switch f := t.Field(i); f.Name {
Joe Tsaie0b77db2020-05-26 11:21:59 -0700151 case genid.SizeCache_goname, genid.SizeCacheA_goname:
Joe Tsai613285c2019-11-13 01:51:27 -0800152 if f.Type == sizecacheType {
153 si.sizecacheOffset = offsetOf(f, mi.Exporter)
154 }
Joe Tsaie0b77db2020-05-26 11:21:59 -0700155 case genid.WeakFields_goname, genid.WeakFieldsA_goname:
Joe Tsai613285c2019-11-13 01:51:27 -0800156 if f.Type == weakFieldsType {
157 si.weakOffset = offsetOf(f, mi.Exporter)
158 }
Joe Tsaie0b77db2020-05-26 11:21:59 -0700159 case genid.UnknownFields_goname, genid.UnknownFieldsA_goname:
Joe Tsai613285c2019-11-13 01:51:27 -0800160 if f.Type == unknownFieldsType {
161 si.unknownOffset = offsetOf(f, mi.Exporter)
162 }
Joe Tsaie0b77db2020-05-26 11:21:59 -0700163 case genid.ExtensionFields_goname, genid.ExtensionFieldsA_goname, genid.ExtensionFieldsB_goname:
Joe Tsai613285c2019-11-13 01:51:27 -0800164 if f.Type == extensionFieldsType {
165 si.extensionOffset = offsetOf(f, mi.Exporter)
166 }
167 default:
168 for _, s := range strings.Split(f.Tag.Get("protobuf"), ",") {
169 if len(s) > 0 && strings.Trim(s, "0123456789") == "" {
170 n, _ := strconv.ParseUint(s, 10, 64)
171 si.fieldsByNumber[pref.FieldNumber(n)] = f
172 continue fieldLoop
173 }
174 }
175 if s := f.Tag.Get("protobuf_oneof"); len(s) > 0 {
176 si.oneofsByName[pref.Name(s)] = f
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700177 continue fieldLoop
178 }
179 }
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700180 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700181
182 // Derive a mapping of oneof wrappers to fields.
Joe Tsai09912272019-07-08 10:38:11 -0700183 oneofWrappers := mi.OneofWrappers
Joe Tsai7dfcffe2020-04-02 10:31:06 -0700184 for _, method := range []string{"XXX_OneofFuncs", "XXX_OneofWrappers"} {
185 if fn, ok := reflect.PtrTo(t).MethodByName(method); ok {
186 for _, v := range fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))}) {
187 if vs, ok := v.Interface().([]interface{}); ok {
188 oneofWrappers = vs
189 }
190 }
191 }
Joe Tsaid7e97bc2018-11-26 12:57:27 -0800192 }
193 for _, v := range oneofWrappers {
194 tf := reflect.TypeOf(v).Elem()
195 f := tf.Field(0)
196 for _, s := range strings.Split(f.Tag.Get("protobuf"), ",") {
197 if len(s) > 0 && strings.Trim(s, "0123456789") == "" {
198 n, _ := strconv.ParseUint(s, 10, 64)
Damien Neil3eaddf02019-05-09 11:33:55 -0700199 si.oneofWrappersByType[tf] = pref.FieldNumber(n)
200 si.oneofWrappersByNumber[pref.FieldNumber(n)] = tf
Joe Tsaid7e97bc2018-11-26 12:57:27 -0800201 break
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700202 }
203 }
204 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700205
Damien Neil3eaddf02019-05-09 11:33:55 -0700206 return si
207}
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700208
Damien Neil16163b42019-08-06 15:43:25 -0700209func (mi *MessageInfo) New() protoreflect.Message {
210 return mi.MessageOf(reflect.New(mi.GoReflectType.Elem()).Interface())
211}
212func (mi *MessageInfo) Zero() protoreflect.Message {
213 return mi.MessageOf(reflect.Zero(mi.GoReflectType).Interface())
214}
215func (mi *MessageInfo) Descriptor() protoreflect.MessageDescriptor { return mi.Desc }