mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 12:32:26 -05:00 
			
		
		
		
	
		
			
	
	
		
			277 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			277 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | // 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" | ||
|  | 	pref "google.golang.org/protobuf/reflect/protoreflect" | ||
|  | 	preg "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 pref.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 []interface{} | ||
|  | 
 | ||
|  | 	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 interface{}, i int) interface{} | ||
|  | 
 | ||
|  | // 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().(pref.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 pref.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[pref.FieldNumber]reflect.StructField | ||
|  | 	oneofsByName          map[pref.Name]reflect.StructField | ||
|  | 	oneofWrappersByType   map[reflect.Type]pref.FieldNumber | ||
|  | 	oneofWrappersByNumber map[pref.FieldNumber]reflect.Type | ||
|  | } | ||
|  | 
 | ||
|  | func (mi *MessageInfo) makeStructInfo(t reflect.Type) structInfo { | ||
|  | 	si := structInfo{ | ||
|  | 		sizecacheOffset: invalidOffset, | ||
|  | 		weakOffset:      invalidOffset, | ||
|  | 		unknownOffset:   invalidOffset, | ||
|  | 		extensionOffset: invalidOffset, | ||
|  | 
 | ||
|  | 		fieldsByNumber:        map[pref.FieldNumber]reflect.StructField{}, | ||
|  | 		oneofsByName:          map[pref.Name]reflect.StructField{}, | ||
|  | 		oneofWrappersByType:   map[reflect.Type]pref.FieldNumber{}, | ||
|  | 		oneofWrappersByNumber: map[pref.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[pref.FieldNumber(n)] = f | ||
|  | 					continue fieldLoop | ||
|  | 				} | ||
|  | 			} | ||
|  | 			if s := f.Tag.Get("protobuf_oneof"); len(s) > 0 { | ||
|  | 				si.oneofsByName[pref.Name(s)] = f | ||
|  | 				continue fieldLoop | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Derive a mapping of oneof wrappers to fields. | ||
|  | 	oneofWrappers := mi.OneofWrappers | ||
|  | 	for _, method := range []string{"XXX_OneofFuncs", "XXX_OneofWrappers"} { | ||
|  | 		if fn, ok := reflect.PtrTo(t).MethodByName(method); ok { | ||
|  | 			for _, v := range fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))}) { | ||
|  | 				if vs, ok := v.Interface().([]interface{}); 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] = pref.FieldNumber(n) | ||
|  | 				si.oneofWrappersByNumber[pref.FieldNumber(n)] = tf | ||
|  | 				break | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return si | ||
|  | } | ||
|  | 
 | ||
|  | func (mi *MessageInfo) New() protoreflect.Message { | ||
|  | 	return mi.MessageOf(reflect.New(mi.GoReflectType.Elem()).Interface()) | ||
|  | } | ||
|  | 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, _ := preg.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 interface{} // 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) | ||
|  | } |