| // 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" |
| |
| pref "github.com/golang/protobuf/v2/reflect/protoreflect" |
| ptype "github.com/golang/protobuf/v2/reflect/prototype" |
| ) |
| |
| // MessageType provides protobuf related functionality for a given Go type |
| // that represents a message. A given instance of MessageType is tied to |
| // exactly one Go type, which must be a pointer to a struct type. |
| type MessageType struct { |
| // Desc is an optionally provided message descriptor. If nil, the descriptor |
| // is lazily derived from the Go type information of generated messages |
| // for the v1 API. |
| // |
| // Once set, this field must never be mutated. |
| Desc pref.MessageDescriptor |
| |
| once sync.Once // protects all unexported fields |
| |
| goType reflect.Type // pointer to struct |
| pbType pref.MessageType // only valid if goType does not implement proto.Message |
| |
| // TODO: Split fields into dense and sparse maps similar to the current |
| // table-driven implementation in v1? |
| fields map[pref.FieldNumber]*fieldInfo |
| } |
| |
| // init lazily initializes the MessageType upon first use and |
| // also checks that the provided pointer p is of the correct Go type. |
| // |
| // It must be called at the start of every exported method. |
| func (mi *MessageType) init(p interface{}) { |
| mi.once.Do(func() { |
| v := reflect.ValueOf(p) |
| t := v.Type() |
| if t.Kind() != reflect.Ptr && t.Elem().Kind() != reflect.Struct { |
| panic(fmt.Sprintf("got %v, want *struct kind", t)) |
| } |
| mi.goType = t |
| |
| // Derive the message descriptor if unspecified. |
| md := mi.Desc |
| if md == nil { |
| // TODO: derive the message type from the Go struct type |
| } |
| |
| // Initialize the Go message type wrapper if the Go type does not |
| // implement the proto.Message interface. |
| // |
| // Otherwise, we assume that the Go type manually implements the |
| // interface and is internally consistent such that: |
| // goType == reflect.New(goType.Elem()).Interface().(proto.Message).ProtoReflect().Type().GoType() |
| // |
| // Generated code ensures that this property holds. |
| if _, ok := p.(pref.ProtoMessage); !ok { |
| mi.pbType = ptype.NewGoMessage(&ptype.GoMessage{ |
| MessageDescriptor: md, |
| New: func(pref.MessageType) pref.ProtoMessage { |
| p := reflect.New(t.Elem()).Interface() |
| return (*message)(mi.dataTypeOf(p)) |
| }, |
| }) |
| } |
| |
| mi.generateFieldFuncs(t.Elem(), md) |
| }) |
| |
| // TODO: Remove this check? This API is primarily used by generated code, |
| // and should not violate this assumption. Leave this check in for now to |
| // provide some sanity checks during development. This can be removed if |
| // it proves to be detrimental to performance. |
| if reflect.TypeOf(p) != mi.goType { |
| panic(fmt.Sprintf("type mismatch: got %T, want %v", p, mi.goType)) |
| } |
| } |
| |
| // generateFieldFuncs generates per-field functions for all common operations |
| // to be performed on each field. It takes in a reflect.Type representing the |
| // Go struct, and a protoreflect.MessageDescriptor to match with the fields |
| // in the struct. |
| // |
| // This code assumes that the struct is well-formed and panics if there are |
| // any discrepancies. |
| func (mi *MessageType) generateFieldFuncs(t reflect.Type, md pref.MessageDescriptor) { |
| // Generate a mapping of field numbers and names to Go struct field or type. |
| fields := map[pref.FieldNumber]reflect.StructField{} |
| oneofs := map[pref.Name]reflect.StructField{} |
| oneofFields := map[pref.FieldNumber]reflect.Type{} |
| special := map[string]reflect.StructField{} |
| fieldLoop: |
| for i := 0; i < t.NumField(); i++ { |
| f := t.Field(i) |
| for _, s := range strings.Split(f.Tag.Get("protobuf"), ",") { |
| if len(s) > 0 && strings.Trim(s, "0123456789") == "" { |
| n, _ := strconv.ParseUint(s, 10, 64) |
| fields[pref.FieldNumber(n)] = f |
| continue fieldLoop |
| } |
| } |
| if s := f.Tag.Get("protobuf_oneof"); len(s) > 0 { |
| oneofs[pref.Name(s)] = f |
| continue fieldLoop |
| } |
| switch f.Name { |
| case "XXX_weak", "XXX_unrecognized", "XXX_sizecache", "XXX_extensions", "XXX_InternalExtensions": |
| special[f.Name] = f |
| continue fieldLoop |
| } |
| } |
| if fn, ok := t.MethodByName("XXX_OneofFuncs"); ok { |
| vs := fn.Func.Call([]reflect.Value{reflect.New(fn.Type.In(0)).Elem()})[3] |
| oneofLoop: |
| for _, v := range vs.Interface().([]interface{}) { |
| 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) |
| oneofFields[pref.FieldNumber(n)] = tf |
| continue oneofLoop |
| } |
| } |
| } |
| } |
| |
| mi.fields = map[pref.FieldNumber]*fieldInfo{} |
| for i := 0; i < md.Fields().Len(); i++ { |
| fd := md.Fields().Get(i) |
| fs := fields[fd.Number()] |
| var fi fieldInfo |
| switch { |
| case fd.IsWeak(): |
| fi = fieldInfoForWeak(fd, special["XXX_weak"]) |
| case fd.OneofType() != nil: |
| fi = fieldInfoForOneof(fd, oneofs[fd.OneofType().Name()], oneofFields[fd.Number()]) |
| case fd.IsMap(): |
| fi = fieldInfoForMap(fd, fs) |
| case fd.Cardinality() == pref.Repeated: |
| fi = fieldInfoForVector(fd, fs) |
| case fd.Kind() == pref.MessageKind || fd.Kind() == pref.GroupKind: |
| fi = fieldInfoForMessage(fd, fs) |
| default: |
| fi = fieldInfoForScalar(fd, fs) |
| } |
| mi.fields[fd.Number()] = &fi |
| } |
| } |
| |
| func (mi *MessageType) MessageOf(p interface{}) pref.Message { |
| mi.init(p) |
| if m, ok := p.(pref.ProtoMessage); ok { |
| // We assume p properly implements protoreflect.Message. |
| // See the comment in MessageType.init regarding pbType. |
| return m.ProtoReflect() |
| } |
| return (*message)(mi.dataTypeOf(p)) |
| } |
| |
| func (mi *MessageType) KnownFieldsOf(p interface{}) pref.KnownFields { |
| mi.init(p) |
| return (*knownFields)(mi.dataTypeOf(p)) |
| } |
| |
| func (mi *MessageType) UnknownFieldsOf(p interface{}) pref.UnknownFields { |
| mi.init(p) |
| return (*unknownFields)(mi.dataTypeOf(p)) |
| } |
| |
| func (mi *MessageType) dataTypeOf(p interface{}) *messageDataType { |
| return &messageDataType{pointerOfIface(&p), mi} |
| } |
| |
| // messageDataType is a tuple of a pointer to the message data and |
| // a pointer to the message type. |
| // |
| // TODO: Unfortunately, we need to close over a pointer and MessageType, |
| // which incurs an an allocation. This pair is similar to a Go interface, |
| // which is essentially a tuple of the same thing. We can make this efficient |
| // with reflect.NamedOf (see https://golang.org/issues/16522). |
| // |
| // With that hypothetical API, we could dynamically create a new named type |
| // that has the same underlying type as MessageType.goType, and |
| // dynamically create methods that close over MessageType. |
| // Since the new type would have the same underlying type, we could directly |
| // convert between pointers of those types, giving us an efficient way to swap |
| // out the method set. |
| // |
| // Barring the ability to dynamically create named types, the workaround is |
| // 1. either to accept the cost of an allocation for this wrapper struct or |
| // 2. generate more types and methods, at the expense of binary size increase. |
| type messageDataType struct { |
| p pointer |
| mi *MessageType |
| } |
| |
| type message messageDataType |
| |
| func (m *message) Type() pref.MessageType { |
| return m.mi.pbType |
| } |
| func (m *message) KnownFields() pref.KnownFields { |
| return (*knownFields)(m) |
| } |
| func (m *message) UnknownFields() pref.UnknownFields { |
| return (*unknownFields)(m) |
| } |
| func (m *message) Unwrap() interface{} { |
| return m.p.asType(m.mi.goType.Elem()).Interface() |
| } |
| func (m *message) Interface() pref.ProtoMessage { |
| return m |
| } |
| func (m *message) ProtoReflect() pref.Message { |
| return m |
| } |
| func (m *message) ProtoMutable() {} |
| |
| type knownFields messageDataType |
| |
| func (fs *knownFields) List() (nums []pref.FieldNumber) { |
| for n, fi := range fs.mi.fields { |
| if fi.has(fs.p) { |
| nums = append(nums, n) |
| } |
| } |
| // TODO: Handle extension fields. |
| return nums |
| } |
| func (fs *knownFields) Len() (cnt int) { |
| for _, fi := range fs.mi.fields { |
| if fi.has(fs.p) { |
| cnt++ |
| } |
| } |
| // TODO: Handle extension fields. |
| return cnt |
| } |
| func (fs *knownFields) Has(n pref.FieldNumber) bool { |
| if fi := fs.mi.fields[n]; fi != nil { |
| return fi.has(fs.p) |
| } |
| // TODO: Handle extension fields. |
| return false |
| } |
| func (fs *knownFields) Get(n pref.FieldNumber) pref.Value { |
| if fi := fs.mi.fields[n]; fi != nil { |
| return fi.get(fs.p) |
| } |
| // TODO: Handle extension fields. |
| return pref.Value{} |
| } |
| func (fs *knownFields) Set(n pref.FieldNumber, v pref.Value) { |
| if fi := fs.mi.fields[n]; fi != nil { |
| fi.set(fs.p, v) |
| return |
| } |
| // TODO: Handle extension fields. |
| panic("invalid field") |
| } |
| func (fs *knownFields) Clear(n pref.FieldNumber) { |
| if fi := fs.mi.fields[n]; fi != nil { |
| fi.clear(fs.p) |
| return |
| } |
| // TODO: Handle extension fields. |
| panic("invalid field") |
| } |
| func (fs *knownFields) Mutable(n pref.FieldNumber) pref.Mutable { |
| if fi := fs.mi.fields[n]; fi != nil { |
| return fi.mutable(fs.p) |
| } |
| // TODO: Handle extension fields. |
| panic("invalid field") |
| } |
| func (fs *knownFields) Range(f func(pref.FieldNumber, pref.Value) bool) { |
| for n, fi := range fs.mi.fields { |
| if fi.has(fs.p) { |
| if !f(n, fi.get(fs.p)) { |
| return |
| } |
| } |
| } |
| // TODO: Handle extension fields. |
| } |
| func (fs *knownFields) ExtensionTypes() pref.ExtensionFieldTypes { |
| return (*extensionFieldTypes)(fs) |
| } |
| |
| type extensionFieldTypes messageDataType // TODO |
| |
| func (fs *extensionFieldTypes) List() []pref.ExtensionType { return nil } |
| func (fs *extensionFieldTypes) Len() int { return 0 } |
| func (fs *extensionFieldTypes) Register(pref.ExtensionType) { return } |
| func (fs *extensionFieldTypes) Remove(pref.ExtensionType) { return } |
| func (fs *extensionFieldTypes) ByNumber(pref.FieldNumber) pref.ExtensionType { return nil } |
| func (fs *extensionFieldTypes) ByName(pref.FullName) pref.ExtensionType { return nil } |
| func (fs *extensionFieldTypes) Range(f func(pref.ExtensionType) bool) { return } |
| |
| type unknownFields messageDataType // TODO |
| |
| func (fs *unknownFields) List() []pref.FieldNumber { return nil } |
| func (fs *unknownFields) Len() int { return 0 } |
| func (fs *unknownFields) Get(n pref.FieldNumber) pref.RawFields { return nil } |
| func (fs *unknownFields) Set(n pref.FieldNumber, b pref.RawFields) { return } |
| func (fs *unknownFields) Range(f func(pref.FieldNumber, pref.RawFields) bool) { return } |
| func (fs *unknownFields) IsSupported() bool { return false } |