| // 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" |
| "strings" |
| "sync" |
| "unicode" |
| |
| ptag "google.golang.org/protobuf/internal/encoding/tag" |
| ptype "google.golang.org/protobuf/internal/prototype" |
| pref "google.golang.org/protobuf/reflect/protoreflect" |
| "google.golang.org/protobuf/reflect/prototype" |
| ) |
| |
| // legacyWrapMessage wraps v as a protoreflect.ProtoMessage, |
| // where v must be a *struct kind and not implement the v2 API already. |
| func legacyWrapMessage(v reflect.Value) pref.ProtoMessage { |
| mt := legacyLoadMessageInfo(v.Type()) |
| return mt.MessageOf(v.Interface()).Interface() |
| } |
| |
| var legacyMessageTypeCache sync.Map // map[reflect.Type]*MessageInfo |
| |
| // legacyLoadMessageInfo dynamically loads a *MessageInfo for t, |
| // where t must be a *struct kind and not implement the v2 API already. |
| func legacyLoadMessageInfo(t reflect.Type) *MessageInfo { |
| // Fast-path: check if a MessageInfo is cached for this concrete type. |
| if mt, ok := legacyMessageTypeCache.Load(t); ok { |
| return mt.(*MessageInfo) |
| } |
| |
| // Slow-path: derive message descriptor and initialize MessageInfo. |
| md := LegacyLoadMessageDesc(t) |
| mt := new(MessageInfo) |
| mt.GoType = t |
| mt.PBType = &prototype.Message{ |
| MessageDescriptor: md, |
| NewMessage: func() pref.Message { |
| return mt.MessageOf(reflect.New(t.Elem()).Interface()) |
| }, |
| } |
| if mt, ok := legacyMessageTypeCache.LoadOrStore(t, mt); ok { |
| return mt.(*MessageInfo) |
| } |
| return mt |
| } |
| |
| var ( |
| legacyMessageDescLock sync.Mutex |
| legacyMessageDescCache sync.Map // map[reflect.Type]protoreflect.MessageDescriptor |
| ) |
| |
| // LegacyLoadMessageDesc returns an MessageDescriptor derived from the Go type, |
| // which must be a *struct kind and not implement the v2 API already. |
| // |
| // This is exported for testing purposes. |
| func LegacyLoadMessageDesc(t reflect.Type) pref.MessageDescriptor { |
| return legacyMessageDescSet{}.Load(t) |
| } |
| |
| type legacyMessageDescSet struct { |
| visited map[reflect.Type]*ptype.StandaloneMessage |
| descs []*ptype.StandaloneMessage |
| types []reflect.Type |
| } |
| |
| func (ms legacyMessageDescSet) Load(t reflect.Type) pref.MessageDescriptor { |
| // Fast-path: check if a MessageDescriptor is cached for this concrete type. |
| if mi, ok := legacyMessageDescCache.Load(t); ok { |
| return mi.(pref.MessageDescriptor) |
| } |
| |
| // Slow-path: initialize MessageDescriptor from the Go type. |
| // |
| // Hold a global lock during message creation to ensure that each Go type |
| // maps to exactly one MessageDescriptor. After obtaining the lock, we must |
| // check again whether the message has already been handled. |
| legacyMessageDescLock.Lock() |
| defer legacyMessageDescLock.Unlock() |
| if mi, ok := legacyMessageDescCache.Load(t); ok { |
| return mi.(pref.MessageDescriptor) |
| } |
| |
| // Processing t recursively populates descs and types with all sub-messages. |
| // The descriptor for the first type is guaranteed to be at the front. |
| ms.processMessage(t) |
| |
| // Within a proto file it is possible for cyclic dependencies to exist |
| // between multiple message types. When these cases arise, the set of |
| // message descriptors must be created together. |
| mds, err := ptype.NewMessages(ms.descs) |
| if err != nil { |
| panic(err) |
| } |
| for i, md := range mds { |
| // Protobuf semantics represents map entries under-the-hood as |
| // pseudo-messages (has a descriptor, but no generated Go type). |
| // Avoid caching these fake messages. |
| if t := ms.types[i]; t.Kind() != reflect.Map { |
| legacyMessageDescCache.Store(t, md) |
| } |
| } |
| return mds[0] |
| } |
| |
| func (ms *legacyMessageDescSet) processMessage(t reflect.Type) pref.MessageDescriptor { |
| // Fast-path: Obtain a placeholder if the message is already processed. |
| if m, ok := ms.visited[t]; ok { |
| return ptype.PlaceholderMessage(m.FullName) |
| } |
| |
| // Slow-path: Walk over the struct fields to derive the message descriptor. |
| if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct || t.Elem().PkgPath() == "" { |
| panic(fmt.Sprintf("got %v, want named *struct kind", t)) |
| } |
| |
| // Derive name and syntax from the raw descriptor. |
| m := new(ptype.StandaloneMessage) |
| mv := reflect.New(t.Elem()).Interface() |
| if _, ok := mv.(pref.ProtoMessage); ok { |
| panic(fmt.Sprintf("%v already implements proto.Message", t)) |
| } |
| if md, ok := mv.(messageV1); ok { |
| b, idxs := md.Descriptor() |
| fd := legacyLoadFileDesc(b) |
| |
| // Derive syntax. |
| switch fd.GetSyntax() { |
| case "proto2", "": |
| m.Syntax = pref.Proto2 |
| case "proto3": |
| m.Syntax = pref.Proto3 |
| } |
| |
| // Derive full name. |
| md := fd.MessageType[idxs[0]] |
| m.FullName = pref.FullName(fd.GetPackage()).Append(pref.Name(md.GetName())) |
| for _, i := range idxs[1:] { |
| md = md.NestedType[i] |
| m.FullName = m.FullName.Append(pref.Name(md.GetName())) |
| } |
| } else { |
| // If the type does not implement messageV1, then the only way to |
| // obtain the full name is through the registry. However, this is |
| // unreliable as some generated messages register with a fork of |
| // golang/protobuf, so the registry may not have this information. |
| m.FullName = legacyDeriveFullName(t.Elem()) |
| m.Syntax = pref.Proto2 |
| |
| // Try to determine if the message is using proto3 by checking scalars. |
| for i := 0; i < t.Elem().NumField(); i++ { |
| f := t.Elem().Field(i) |
| if tag := f.Tag.Get("protobuf"); tag != "" { |
| switch f.Type.Kind() { |
| case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String: |
| m.Syntax = pref.Proto3 |
| } |
| for _, s := range strings.Split(tag, ",") { |
| if s == "proto3" { |
| m.Syntax = pref.Proto3 |
| } |
| } |
| } |
| } |
| } |
| ms.visit(m, t) |
| |
| // Obtain a list of oneof wrapper types. |
| var oneofWrappers []reflect.Type |
| if fn, ok := t.MethodByName("XXX_OneofFuncs"); ok { |
| vs := fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[3] |
| for _, v := range vs.Interface().([]interface{}) { |
| oneofWrappers = append(oneofWrappers, reflect.TypeOf(v)) |
| } |
| } |
| if fn, ok := t.MethodByName("XXX_OneofWrappers"); ok { |
| vs := fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0] |
| for _, v := range vs.Interface().([]interface{}) { |
| oneofWrappers = append(oneofWrappers, reflect.TypeOf(v)) |
| } |
| } |
| |
| // Obtain a list of the extension ranges. |
| if fn, ok := t.MethodByName("ExtensionRangeArray"); ok { |
| vs := fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0] |
| for i := 0; i < vs.Len(); i++ { |
| v := vs.Index(i) |
| m.ExtensionRanges = append(m.ExtensionRanges, [2]pref.FieldNumber{ |
| pref.FieldNumber(v.FieldByName("Start").Int()), |
| pref.FieldNumber(v.FieldByName("End").Int() + 1), |
| }) |
| } |
| } |
| |
| // Derive the message fields by inspecting the struct fields. |
| for i := 0; i < t.Elem().NumField(); i++ { |
| f := t.Elem().Field(i) |
| if tag := f.Tag.Get("protobuf"); tag != "" { |
| tagKey := f.Tag.Get("protobuf_key") |
| tagVal := f.Tag.Get("protobuf_val") |
| m.Fields = append(m.Fields, ms.parseField(tag, tagKey, tagVal, f.Type, m)) |
| } |
| if tag := f.Tag.Get("protobuf_oneof"); tag != "" { |
| name := pref.Name(tag) |
| m.Oneofs = append(m.Oneofs, ptype.Oneof{Name: name}) |
| for _, t := range oneofWrappers { |
| if t.Implements(f.Type) { |
| f := t.Elem().Field(0) |
| if tag := f.Tag.Get("protobuf"); tag != "" { |
| ft := ms.parseField(tag, "", "", f.Type, m) |
| ft.OneofName = name |
| m.Fields = append(m.Fields, ft) |
| } |
| } |
| } |
| } |
| } |
| |
| return ptype.PlaceholderMessage(m.FullName) |
| } |
| |
| func (ms *legacyMessageDescSet) parseField(tag, tagKey, tagVal string, goType reflect.Type, parent *ptype.StandaloneMessage) ptype.Field { |
| t := goType |
| isOptional := t.Kind() == reflect.Ptr && t.Elem().Kind() != reflect.Struct |
| isRepeated := t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8 |
| if isOptional || isRepeated { |
| t = t.Elem() |
| } |
| f := ptag.Unmarshal(tag, t) |
| |
| // Populate EnumType and MessageType. |
| if f.EnumType == nil && f.Kind == pref.EnumKind { |
| if ev, ok := reflect.Zero(t).Interface().(pref.Enum); ok { |
| f.EnumType = ev.Descriptor() |
| } else { |
| f.EnumType = LegacyLoadEnumDesc(t) |
| } |
| } |
| if f.MessageType == nil && (f.Kind == pref.MessageKind || f.Kind == pref.GroupKind) { |
| if mv, ok := reflect.Zero(t).Interface().(pref.ProtoMessage); ok { |
| f.MessageType = mv.ProtoReflect().Descriptor() |
| } else if t.Kind() == reflect.Map { |
| m := &ptype.StandaloneMessage{ |
| Syntax: parent.Syntax, |
| FullName: parent.FullName.Append(legacyMapEntryName(f.Name)), |
| IsMapEntry: true, |
| Fields: []ptype.Field{ |
| ms.parseField(tagKey, "", "", t.Key(), nil), |
| ms.parseField(tagVal, "", "", t.Elem(), nil), |
| }, |
| } |
| ms.visit(m, t) |
| f.MessageType = ptype.PlaceholderMessage(m.FullName) |
| } else if mv, ok := legacyMessageDescCache.Load(t); ok { |
| f.MessageType = mv.(pref.MessageDescriptor) |
| } else { |
| f.MessageType = ms.processMessage(t) |
| } |
| } |
| return f |
| } |
| |
| func (ms *legacyMessageDescSet) visit(m *ptype.StandaloneMessage, t reflect.Type) { |
| if ms.visited == nil { |
| ms.visited = make(map[reflect.Type]*ptype.StandaloneMessage) |
| } |
| if t.Kind() != reflect.Map { |
| ms.visited[t] = m |
| } |
| ms.descs = append(ms.descs, m) |
| ms.types = append(ms.types, t) |
| } |
| |
| // legacyDeriveFullName derives a fully qualified protobuf name for the given Go type |
| // The provided name is not guaranteed to be stable nor universally unique. |
| // It should be sufficiently unique within a program. |
| func legacyDeriveFullName(t reflect.Type) pref.FullName { |
| sanitize := func(r rune) rune { |
| switch { |
| case r == '/': |
| return '.' |
| case 'a' <= r && r <= 'z', 'A' <= r && r <= 'Z', '0' <= r && r <= '9': |
| return r |
| default: |
| return '_' |
| } |
| } |
| prefix := strings.Map(sanitize, t.PkgPath()) |
| suffix := strings.Map(sanitize, t.Name()) |
| if suffix == "" { |
| suffix = fmt.Sprintf("UnknownX%X", reflect.ValueOf(t).Pointer()) |
| } |
| |
| ss := append(strings.Split(prefix, "."), suffix) |
| for i, s := range ss { |
| if s == "" || ('0' <= s[0] && s[0] <= '9') { |
| ss[i] = "x" + s |
| } |
| } |
| return pref.FullName(strings.Join(ss, ".")) |
| } |
| |
| // legacyMapEntryName derives the message name for a map field of a given name. |
| // This is identical to MapEntryName from parser.cc in the protoc source. |
| func legacyMapEntryName(s pref.Name) pref.Name { |
| var b []byte |
| nextUpper := true |
| for i := 0; i < len(s); i++ { |
| if c := s[i]; c == '_' { |
| nextUpper = true |
| } else { |
| if nextUpper { |
| c = byte(unicode.ToUpper(rune(c))) |
| nextUpper = false |
| } |
| b = append(b, c) |
| } |
| } |
| return pref.Name(append(b, "Entry"...)) |
| } |