| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- // 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"
- "sync"
- "google.golang.org/protobuf/internal/descfmt"
- ptag "google.golang.org/protobuf/internal/encoding/tag"
- "google.golang.org/protobuf/internal/filedesc"
- pvalue "google.golang.org/protobuf/internal/value"
- pref "google.golang.org/protobuf/reflect/protoreflect"
- preg "google.golang.org/protobuf/reflect/protoregistry"
- "google.golang.org/protobuf/reflect/prototype"
- piface "google.golang.org/protobuf/runtime/protoiface"
- )
- // legacyExtensionDescKey is a comparable version of protoiface.ExtensionDescV1
- // suitable for use as a key in a map.
- type legacyExtensionDescKey struct {
- typeV2 pref.ExtensionType
- extendedType reflect.Type
- extensionType reflect.Type
- field int32
- name string
- tag string
- filename string
- }
- func legacyExtensionDescKeyOf(d *piface.ExtensionDescV1) legacyExtensionDescKey {
- return legacyExtensionDescKey{
- d.Type,
- reflect.TypeOf(d.ExtendedType),
- reflect.TypeOf(d.ExtensionType),
- d.Field, d.Name, d.Tag, d.Filename,
- }
- }
- var (
- legacyExtensionTypeCache sync.Map // map[legacyExtensionDescKey]protoreflect.ExtensionType
- legacyExtensionDescCache sync.Map // map[protoreflect.ExtensionType]*protoiface.ExtensionDescV1
- )
- // legacyExtensionDescFromType converts a v2 protoreflect.ExtensionType to a
- // protoiface.ExtensionDescV1. The returned ExtensionDesc must not be mutated.
- func legacyExtensionDescFromType(xt pref.ExtensionType) *piface.ExtensionDescV1 {
- // Fast-path: check whether an extension desc is already nested within.
- if xt, ok := xt.(interface {
- ProtoLegacyExtensionDesc() *piface.ExtensionDescV1
- }); ok {
- if d := xt.ProtoLegacyExtensionDesc(); d != nil {
- return d
- }
- }
- // Fast-path: check the cache for whether this ExtensionType has already
- // been converted to a legacy descriptor.
- if d, ok := legacyExtensionDescCache.Load(xt); ok {
- return d.(*piface.ExtensionDescV1)
- }
- // Determine the parent type if possible.
- var parent piface.MessageV1
- messageName := xt.Descriptor().ContainingMessage().FullName()
- if mt, _ := preg.GlobalTypes.FindMessageByName(messageName); mt != nil {
- // Create a new parent message and unwrap it if possible.
- mv := mt.New().Interface()
- t := reflect.TypeOf(mv)
- if mv, ok := mv.(pvalue.Unwrapper); ok {
- t = reflect.TypeOf(mv.ProtoUnwrap())
- }
- // Check whether the message implements the legacy v1 Message interface.
- mz := reflect.Zero(t).Interface()
- if mz, ok := mz.(piface.MessageV1); ok {
- parent = mz
- }
- }
- // Determine the v1 extension type, which is unfortunately not the same as
- // the v2 ExtensionType.GoType.
- extType := xt.GoType()
- switch extType.Kind() {
- case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
- extType = reflect.PtrTo(extType) // T -> *T for singular scalar fields
- case reflect.Ptr:
- if extType.Elem().Kind() == reflect.Slice {
- extType = extType.Elem() // *[]T -> []T for repeated fields
- }
- }
- // Reconstruct the legacy enum full name, which is an odd mixture of the
- // proto package name with the Go type name.
- var enumName string
- if xt.Descriptor().Kind() == pref.EnumKind {
- // Derive Go type name.
- t := extType
- if t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice {
- t = t.Elem()
- }
- enumName = t.Name()
- // Derive the proto package name.
- // For legacy enums, obtain the proto package from the raw descriptor.
- var protoPkg string
- if fd := xt.Descriptor().Enum().ParentFile(); fd != nil {
- protoPkg = string(fd.Package())
- }
- if ed, ok := reflect.Zero(t).Interface().(enumV1); ok && protoPkg == "" {
- b, _ := ed.EnumDescriptor()
- protoPkg = string(legacyLoadFileDesc(b).Package())
- }
- if protoPkg != "" {
- enumName = protoPkg + "." + enumName
- }
- }
- // Derive the proto file that the extension was declared within.
- var filename string
- if fd := xt.Descriptor().ParentFile(); fd != nil {
- filename = fd.Path()
- }
- // Construct and return a ExtensionDescV1.
- d := &piface.ExtensionDescV1{
- Type: xt,
- ExtendedType: parent,
- ExtensionType: reflect.Zero(extType).Interface(),
- Field: int32(xt.Descriptor().Number()),
- Name: string(xt.Descriptor().FullName()),
- Tag: ptag.Marshal(xt.Descriptor(), enumName),
- Filename: filename,
- }
- if d, ok := legacyExtensionDescCache.LoadOrStore(xt, d); ok {
- return d.(*piface.ExtensionDescV1)
- }
- return d
- }
- // legacyExtensionTypeFromDesc converts a protoiface.ExtensionDescV1 to a
- // v2 protoreflect.ExtensionType. The returned descriptor type takes ownership
- // of the input extension desc. The input must not be mutated so long as the
- // returned type is still in use.
- func legacyExtensionTypeFromDesc(d *piface.ExtensionDescV1) pref.ExtensionType {
- // Fast-path: check whether an extension type is already nested within.
- if d.Type != nil {
- return d.Type
- }
- // Fast-path: check the cache for whether this ExtensionType has already
- // been converted from a legacy descriptor.
- dk := legacyExtensionDescKeyOf(d)
- if t, ok := legacyExtensionTypeCache.Load(dk); ok {
- return t.(pref.ExtensionType)
- }
- // Resolve enum or message dependencies.
- var ed pref.EnumDescriptor
- var md pref.MessageDescriptor
- t := reflect.TypeOf(d.ExtensionType)
- 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()
- }
- switch v := reflect.Zero(t).Interface().(type) {
- case pref.Enum:
- ed = v.Descriptor()
- case enumV1:
- ed = LegacyLoadEnumDesc(t)
- case pref.ProtoMessage:
- md = v.ProtoReflect().Descriptor()
- case messageV1:
- md = LegacyLoadMessageDesc(t)
- }
- // Derive basic field information from the struct tag.
- var evs pref.EnumValueDescriptors
- if ed != nil {
- evs = ed.Values()
- }
- fd := ptag.Unmarshal(d.Tag, t, evs).(*filedesc.Field)
- // Construct a v2 ExtensionType.
- xd := &filedesc.Extension{L2: new(filedesc.ExtensionL2)}
- xd.L0.ParentFile = filedesc.SurrogateProto2
- xd.L0.FullName = pref.FullName(d.Name)
- xd.L1.Number = pref.FieldNumber(d.Field)
- xd.L2.Cardinality = fd.L1.Cardinality
- xd.L1.Kind = fd.L1.Kind
- xd.L2.IsPacked = fd.L1.IsPacked
- xd.L2.Default = fd.L1.Default
- xd.L1.Extendee = Export{}.MessageDescriptorOf(d.ExtendedType)
- xd.L2.Enum = ed
- xd.L2.Message = md
- xt := LegacyExtensionTypeOf(xd, t)
- // Cache the conversion for both directions.
- legacyExtensionDescCache.LoadOrStore(xt, d)
- if xt, ok := legacyExtensionTypeCache.LoadOrStore(dk, xt); ok {
- return xt.(pref.ExtensionType)
- }
- return xt
- }
- // LegacyExtensionTypeOf returns a protoreflect.ExtensionType where the
- // element type of the field is t. The type t must be provided if the field
- // is an enum or message.
- //
- // This is exported for testing purposes.
- func LegacyExtensionTypeOf(xd pref.ExtensionDescriptor, t reflect.Type) pref.ExtensionType {
- var conv pvalue.Converter
- var isLegacy bool
- xt := &prototype.Extension{ExtensionDescriptor: xd}
- switch xd.Kind() {
- case pref.EnumKind:
- conv, isLegacy = newConverter(t, xd.Kind())
- xt.NewEnum = conv.NewEnum
- case pref.MessageKind, pref.GroupKind:
- conv, isLegacy = newConverter(t, xd.Kind())
- xt.NewMessage = conv.NewMessage
- default:
- // Extension types for non-enums and non-messages are simple.
- return &prototype.Extension{ExtensionDescriptor: xd}
- }
- if !isLegacy {
- return xt
- }
- // Wrap ExtensionType such that GoType presents the legacy Go type.
- xt2 := &legacyExtensionType{ExtensionType: xt}
- if xd.Cardinality() != pref.Repeated {
- xt2.typ = t
- xt2.new = func() pref.Value {
- return xt.New()
- }
- xt2.valueOf = func(v interface{}) pref.Value {
- if reflect.TypeOf(v) != xt2.typ {
- panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
- }
- if xd.Kind() == pref.EnumKind {
- return xt.ValueOf(Export{}.EnumOf(v))
- } else {
- return xt.ValueOf(Export{}.MessageOf(v).Interface())
- }
- }
- xt2.interfaceOf = func(v pref.Value) interface{} {
- return xt.InterfaceOf(v).(pvalue.Unwrapper).ProtoUnwrap()
- }
- } else {
- xt2.typ = reflect.PtrTo(reflect.SliceOf(t))
- xt2.new = func() pref.Value {
- v := reflect.New(xt2.typ.Elem()).Interface()
- return pref.ValueOf(pvalue.ListOf(v, conv))
- }
- xt2.valueOf = func(v interface{}) pref.Value {
- if reflect.TypeOf(v) != xt2.typ {
- panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
- }
- return pref.ValueOf(pvalue.ListOf(v, conv))
- }
- xt2.interfaceOf = func(pv pref.Value) interface{} {
- v := pv.List().(pvalue.Unwrapper).ProtoUnwrap()
- if reflect.TypeOf(v) != xt2.typ {
- panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
- }
- return v
- }
- }
- return xt2
- }
- type legacyExtensionType struct {
- pref.ExtensionType
- typ reflect.Type
- new func() pref.Value
- valueOf func(interface{}) pref.Value
- interfaceOf func(pref.Value) interface{}
- }
- func (x *legacyExtensionType) GoType() reflect.Type { return x.typ }
- func (x *legacyExtensionType) New() pref.Value { return x.new() }
- func (x *legacyExtensionType) ValueOf(v interface{}) pref.Value { return x.valueOf(v) }
- func (x *legacyExtensionType) InterfaceOf(v pref.Value) interface{} { return x.interfaceOf(v) }
- func (x *legacyExtensionType) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, x.Descriptor()) }
|