blob: 786f43496df3308da3d20fa69a97a98688806583 [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 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
Joe Tsai378c1322019-04-25 23:48:08 -070096type (
97 SizeCache = int32
Joe Tsai3d8e3692019-04-08 13:52:14 -070098 WeakFields = map[int32]piface.MessageV1
Joe Tsai378c1322019-04-25 23:48:08 -070099 UnknownFields = []byte
100 ExtensionFields = map[int32]ExtensionField
101)
102
103var (
104 sizecacheType = reflect.TypeOf(SizeCache(0))
Joe Tsai3d8e3692019-04-08 13:52:14 -0700105 weakFieldsType = reflect.TypeOf(WeakFields(nil))
Joe Tsai378c1322019-04-25 23:48:08 -0700106 unknownFieldsType = reflect.TypeOf(UnknownFields(nil))
107 extensionFieldsType = reflect.TypeOf(ExtensionFields(nil))
108)
Damien Neilc37adef2019-04-01 13:49:56 -0700109
Damien Neil3eaddf02019-05-09 11:33:55 -0700110type structInfo struct {
Joe Tsai93fd9682019-07-06 13:02:14 -0700111 sizecacheOffset offset
Joe Tsai3d8e3692019-04-08 13:52:14 -0700112 weakOffset offset
Joe Tsai93fd9682019-07-06 13:02:14 -0700113 unknownOffset offset
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700114 extensionOffset offset
Joe Tsai93fd9682019-07-06 13:02:14 -0700115
Damien Neil3eaddf02019-05-09 11:33:55 -0700116 fieldsByNumber map[pref.FieldNumber]reflect.StructField
117 oneofsByName map[pref.Name]reflect.StructField
118 oneofWrappersByType map[reflect.Type]pref.FieldNumber
119 oneofWrappersByNumber map[pref.FieldNumber]reflect.Type
120}
121
Joe Tsai4fe96632019-05-22 05:12:36 -0400122func (mi *MessageInfo) makeStructInfo(t reflect.Type) structInfo {
Damien Neil3eaddf02019-05-09 11:33:55 -0700123 si := structInfo{
Joe Tsai93fd9682019-07-06 13:02:14 -0700124 sizecacheOffset: invalidOffset,
Joe Tsai3d8e3692019-04-08 13:52:14 -0700125 weakOffset: invalidOffset,
Joe Tsai93fd9682019-07-06 13:02:14 -0700126 unknownOffset: invalidOffset,
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700127 extensionOffset: invalidOffset,
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 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700134
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700135fieldLoop:
136 for i := 0; i < t.NumField(); i++ {
Joe Tsai613285c2019-11-13 01:51:27 -0800137 switch f := t.Field(i); f.Name {
138 case "sizeCache", "XXX_sizecache":
139 if f.Type == sizecacheType {
140 si.sizecacheOffset = offsetOf(f, mi.Exporter)
141 }
142 case "weakFields", "XXX_weak":
143 if f.Type == weakFieldsType {
144 si.weakOffset = offsetOf(f, mi.Exporter)
145 }
146 case "unknownFields", "XXX_unrecognized":
147 if f.Type == unknownFieldsType {
148 si.unknownOffset = offsetOf(f, mi.Exporter)
149 }
150 case "extensionFields", "XXX_InternalExtensions", "XXX_extensions":
151 if f.Type == extensionFieldsType {
152 si.extensionOffset = offsetOf(f, mi.Exporter)
153 }
154 default:
155 for _, s := range strings.Split(f.Tag.Get("protobuf"), ",") {
156 if len(s) > 0 && strings.Trim(s, "0123456789") == "" {
157 n, _ := strconv.ParseUint(s, 10, 64)
158 si.fieldsByNumber[pref.FieldNumber(n)] = f
159 continue fieldLoop
160 }
161 }
162 if s := f.Tag.Get("protobuf_oneof"); len(s) > 0 {
163 si.oneofsByName[pref.Name(s)] = f
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700164 continue fieldLoop
165 }
166 }
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700167 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700168
169 // Derive a mapping of oneof wrappers to fields.
Joe Tsai09912272019-07-08 10:38:11 -0700170 oneofWrappers := mi.OneofWrappers
Joe Tsai2c870bb2018-10-17 11:46:52 -0700171 if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofFuncs"); ok {
Joe Tsaid7e97bc2018-11-26 12:57:27 -0800172 oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[3].Interface().([]interface{})
173 }
174 if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofWrappers"); ok {
175 oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0].Interface().([]interface{})
176 }
177 for _, v := range oneofWrappers {
178 tf := reflect.TypeOf(v).Elem()
179 f := tf.Field(0)
180 for _, s := range strings.Split(f.Tag.Get("protobuf"), ",") {
181 if len(s) > 0 && strings.Trim(s, "0123456789") == "" {
182 n, _ := strconv.ParseUint(s, 10, 64)
Damien Neil3eaddf02019-05-09 11:33:55 -0700183 si.oneofWrappersByType[tf] = pref.FieldNumber(n)
184 si.oneofWrappersByNumber[pref.FieldNumber(n)] = tf
Joe Tsaid7e97bc2018-11-26 12:57:27 -0800185 break
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700186 }
187 }
188 }
Joe Tsai93fd9682019-07-06 13:02:14 -0700189
Damien Neil3eaddf02019-05-09 11:33:55 -0700190 return si
191}
Joe Tsaifa02f4e2018-09-12 16:20:37 -0700192
Damien Neil16163b42019-08-06 15:43:25 -0700193func (mi *MessageInfo) New() protoreflect.Message {
194 return mi.MessageOf(reflect.New(mi.GoReflectType.Elem()).Interface())
195}
196func (mi *MessageInfo) Zero() protoreflect.Message {
197 return mi.MessageOf(reflect.Zero(mi.GoReflectType).Interface())
198}
199func (mi *MessageInfo) Descriptor() protoreflect.MessageDescriptor { return mi.Desc }