| // Copyright 2018 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package impl | |
| import ( | |
| "fmt" | |
| "reflect" | |
| "strconv" | |
| "strings" | |
| "sync" | |
| "sync/atomic" | |
| "google.golang.org/protobuf/internal/genid" | |
| "google.golang.org/protobuf/reflect/protoreflect" | |
| "google.golang.org/protobuf/reflect/protoregistry" | |
| ) | |
| // MessageInfo provides protobuf related functionality for a given Go type | |
| // that represents a message. A given instance of MessageInfo is tied to | |
| // exactly one Go type, which must be a pointer to a struct type. | |
| // | |
| // The exported fields must be populated before any methods are called | |
| // and cannot be mutated after set. | |
| type MessageInfo struct { | |
| // GoReflectType is the underlying message Go type and must be populated. | |
| GoReflectType reflect.Type // pointer to struct | |
| // Desc is the underlying message descriptor type and must be populated. | |
| Desc protoreflect.MessageDescriptor | |
| // Exporter must be provided in a purego environment in order to provide | |
| // access to unexported fields. | |
| Exporter exporter | |
| // OneofWrappers is list of pointers to oneof wrapper struct types. | |
| OneofWrappers []any | |
| initMu sync.Mutex // protects all unexported fields | |
| initDone uint32 | |
| reflectMessageInfo // for reflection implementation | |
| coderMessageInfo // for fast-path method implementations | |
| } | |
| // exporter is a function that returns a reference to the ith field of v, | |
| // where v is a pointer to a struct. It returns nil if it does not support | |
| // exporting the requested field (e.g., already exported). | |
| type exporter func(v any, i int) any | |
| // getMessageInfo returns the MessageInfo for any message type that | |
| // is generated by our implementation of protoc-gen-go (for v2 and on). | |
| // If it is unable to obtain a MessageInfo, it returns nil. | |
| func getMessageInfo(mt reflect.Type) *MessageInfo { | |
| m, ok := reflect.Zero(mt).Interface().(protoreflect.ProtoMessage) | |
| if !ok { | |
| return nil | |
| } | |
| mr, ok := m.ProtoReflect().(interface{ ProtoMessageInfo() *MessageInfo }) | |
| if !ok { | |
| return nil | |
| } | |
| return mr.ProtoMessageInfo() | |
| } | |
| func (mi *MessageInfo) init() { | |
| // This function is called in the hot path. Inline the sync.Once logic, | |
| // since allocating a closure for Once.Do is expensive. | |
| // Keep init small to ensure that it can be inlined. | |
| if atomic.LoadUint32(&mi.initDone) == 0 { | |
| mi.initOnce() | |
| } | |
| } | |
| func (mi *MessageInfo) initOnce() { | |
| mi.initMu.Lock() | |
| defer mi.initMu.Unlock() | |
| if mi.initDone == 1 { | |
| return | |
| } | |
| t := mi.GoReflectType | |
| if t.Kind() != reflect.Ptr && t.Elem().Kind() != reflect.Struct { | |
| panic(fmt.Sprintf("got %v, want *struct kind", t)) | |
| } | |
| t = t.Elem() | |
| si := mi.makeStructInfo(t) | |
| mi.makeReflectFuncs(t, si) | |
| mi.makeCoderMethods(t, si) | |
| atomic.StoreUint32(&mi.initDone, 1) | |
| } | |
| // getPointer returns the pointer for a message, which should be of | |
| // the type of the MessageInfo. If the message is of a different type, | |
| // it returns ok==false. | |
| func (mi *MessageInfo) getPointer(m protoreflect.Message) (p pointer, ok bool) { | |
| switch m := m.(type) { | |
| case *messageState: | |
| return m.pointer(), m.messageInfo() == mi | |
| case *messageReflectWrapper: | |
| return m.pointer(), m.messageInfo() == mi | |
| } | |
| return pointer{}, false | |
| } | |
| type ( | |
| SizeCache = int32 | |
| WeakFields = map[int32]protoreflect.ProtoMessage | |
| UnknownFields = unknownFieldsA // TODO: switch to unknownFieldsB | |
| unknownFieldsA = []byte | |
| unknownFieldsB = *[]byte | |
| ExtensionFields = map[int32]ExtensionField | |
| ) | |
| var ( | |
| sizecacheType = reflect.TypeOf(SizeCache(0)) | |
| weakFieldsType = reflect.TypeOf(WeakFields(nil)) | |
| unknownFieldsAType = reflect.TypeOf(unknownFieldsA(nil)) | |
| unknownFieldsBType = reflect.TypeOf(unknownFieldsB(nil)) | |
| extensionFieldsType = reflect.TypeOf(ExtensionFields(nil)) | |
| ) | |
| type structInfo struct { | |
| sizecacheOffset offset | |
| sizecacheType reflect.Type | |
| weakOffset offset | |
| weakType reflect.Type | |
| unknownOffset offset | |
| unknownType reflect.Type | |
| extensionOffset offset | |
| extensionType reflect.Type | |
| fieldsByNumber map[protoreflect.FieldNumber]reflect.StructField | |
| oneofsByName map[protoreflect.Name]reflect.StructField | |
| oneofWrappersByType map[reflect.Type]protoreflect.FieldNumber | |
| oneofWrappersByNumber map[protoreflect.FieldNumber]reflect.Type | |
| } | |
| func (mi *MessageInfo) makeStructInfo(t reflect.Type) structInfo { | |
| si := structInfo{ | |
| sizecacheOffset: invalidOffset, | |
| weakOffset: invalidOffset, | |
| unknownOffset: invalidOffset, | |
| extensionOffset: invalidOffset, | |
| fieldsByNumber: map[protoreflect.FieldNumber]reflect.StructField{}, | |
| oneofsByName: map[protoreflect.Name]reflect.StructField{}, | |
| oneofWrappersByType: map[reflect.Type]protoreflect.FieldNumber{}, | |
| oneofWrappersByNumber: map[protoreflect.FieldNumber]reflect.Type{}, | |
| } | |
| fieldLoop: | |
| for i := 0; i < t.NumField(); i++ { | |
| switch f := t.Field(i); f.Name { | |
| case genid.SizeCache_goname, genid.SizeCacheA_goname: | |
| if f.Type == sizecacheType { | |
| si.sizecacheOffset = offsetOf(f, mi.Exporter) | |
| si.sizecacheType = f.Type | |
| } | |
| case genid.WeakFields_goname, genid.WeakFieldsA_goname: | |
| if f.Type == weakFieldsType { | |
| si.weakOffset = offsetOf(f, mi.Exporter) | |
| si.weakType = f.Type | |
| } | |
| case genid.UnknownFields_goname, genid.UnknownFieldsA_goname: | |
| if f.Type == unknownFieldsAType || f.Type == unknownFieldsBType { | |
| si.unknownOffset = offsetOf(f, mi.Exporter) | |
| si.unknownType = f.Type | |
| } | |
| case genid.ExtensionFields_goname, genid.ExtensionFieldsA_goname, genid.ExtensionFieldsB_goname: | |
| if f.Type == extensionFieldsType { | |
| si.extensionOffset = offsetOf(f, mi.Exporter) | |
| si.extensionType = f.Type | |
| } | |
| default: | |
| for _, s := range strings.Split(f.Tag.Get("protobuf"), ",") { | |
| if len(s) > 0 && strings.Trim(s, "0123456789") == "" { | |
| n, _ := strconv.ParseUint(s, 10, 64) | |
| si.fieldsByNumber[protoreflect.FieldNumber(n)] = f | |
| continue fieldLoop | |
| } | |
| } | |
| if s := f.Tag.Get("protobuf_oneof"); len(s) > 0 { | |
| si.oneofsByName[protoreflect.Name(s)] = f | |
| continue fieldLoop | |
| } | |
| } | |
| } | |
| // Derive a mapping of oneof wrappers to fields. | |
| oneofWrappers := mi.OneofWrappers | |
| methods := make([]reflect.Method, 0, 2) | |
| if m, ok := reflect.PtrTo(t).MethodByName("XXX_OneofFuncs"); ok { | |
| methods = append(methods, m) | |
| } | |
| if m, ok := reflect.PtrTo(t).MethodByName("XXX_OneofWrappers"); ok { | |
| methods = append(methods, m) | |
| } | |
| for _, fn := range methods { | |
| for _, v := range fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))}) { | |
| if vs, ok := v.Interface().([]any); ok { | |
| oneofWrappers = vs | |
| } | |
| } | |
| } | |
| for _, v := range oneofWrappers { | |
| tf := reflect.TypeOf(v).Elem() | |
| f := tf.Field(0) | |
| for _, s := range strings.Split(f.Tag.Get("protobuf"), ",") { | |
| if len(s) > 0 && strings.Trim(s, "0123456789") == "" { | |
| n, _ := strconv.ParseUint(s, 10, 64) | |
| si.oneofWrappersByType[tf] = protoreflect.FieldNumber(n) | |
| si.oneofWrappersByNumber[protoreflect.FieldNumber(n)] = tf | |
| break | |
| } | |
| } | |
| } | |
| return si | |
| } | |
| func (mi *MessageInfo) New() protoreflect.Message { | |
| m := reflect.New(mi.GoReflectType.Elem()).Interface() | |
| if r, ok := m.(protoreflect.ProtoMessage); ok { | |
| return r.ProtoReflect() | |
| } | |
| return mi.MessageOf(m) | |
| } | |
| func (mi *MessageInfo) Zero() protoreflect.Message { | |
| return mi.MessageOf(reflect.Zero(mi.GoReflectType).Interface()) | |
| } | |
| func (mi *MessageInfo) Descriptor() protoreflect.MessageDescriptor { | |
| return mi.Desc | |
| } | |
| func (mi *MessageInfo) Enum(i int) protoreflect.EnumType { | |
| mi.init() | |
| fd := mi.Desc.Fields().Get(i) | |
| return Export{}.EnumTypeOf(mi.fieldTypes[fd.Number()]) | |
| } | |
| func (mi *MessageInfo) Message(i int) protoreflect.MessageType { | |
| mi.init() | |
| fd := mi.Desc.Fields().Get(i) | |
| switch { | |
| case fd.IsWeak(): | |
| mt, _ := protoregistry.GlobalTypes.FindMessageByName(fd.Message().FullName()) | |
| return mt | |
| case fd.IsMap(): | |
| return mapEntryType{fd.Message(), mi.fieldTypes[fd.Number()]} | |
| default: | |
| return Export{}.MessageTypeOf(mi.fieldTypes[fd.Number()]) | |
| } | |
| } | |
| type mapEntryType struct { | |
| desc protoreflect.MessageDescriptor | |
| valType any // zero value of enum or message type | |
| } | |
| func (mt mapEntryType) New() protoreflect.Message { | |
| return nil | |
| } | |
| func (mt mapEntryType) Zero() protoreflect.Message { | |
| return nil | |
| } | |
| func (mt mapEntryType) Descriptor() protoreflect.MessageDescriptor { | |
| return mt.desc | |
| } | |
| func (mt mapEntryType) Enum(i int) protoreflect.EnumType { | |
| fd := mt.desc.Fields().Get(i) | |
| if fd.Enum() == nil { | |
| return nil | |
| } | |
| return Export{}.EnumTypeOf(mt.valType) | |
| } | |
| func (mt mapEntryType) Message(i int) protoreflect.MessageType { | |
| fd := mt.desc.Fields().Get(i) | |
| if fd.Message() == nil { | |
| return nil | |
| } | |
| return Export{}.MessageTypeOf(mt.valType) | |
| } |