blob: ec5420d2449ce50ec1e9b9855d552ff288012870 [file] [log] [blame]
Joe Tsai08e00302018-11-26 22:32:06 -08001// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Joe Tsai21ade492019-05-22 13:42:54 -04005package impl
Joe Tsai08e00302018-11-26 22:32:06 -08006
7import (
Joe Tsai08e00302018-11-26 22:32:06 -08008 "reflect"
Joe Tsai62517cc2018-12-04 14:00:01 -08009 "sync"
Joe Tsai08e00302018-11-26 22:32:06 -080010
Joe Tsai945a1702019-07-20 14:57:56 -070011 "google.golang.org/protobuf/internal/encoding/messageset"
Damien Neile89e6242019-05-13 23:55:40 -070012 ptag "google.golang.org/protobuf/internal/encoding/tag"
Joe Tsaid8881392019-06-06 13:01:53 -070013 "google.golang.org/protobuf/internal/filedesc"
Damien Neile89e6242019-05-13 23:55:40 -070014 pref "google.golang.org/protobuf/reflect/protoreflect"
15 preg "google.golang.org/protobuf/reflect/protoregistry"
16 piface "google.golang.org/protobuf/runtime/protoiface"
Joe Tsai08e00302018-11-26 22:32:06 -080017)
18
Damien Neilf1e905b2019-08-08 15:45:59 -070019var legacyExtensionInfoCache sync.Map // map[protoreflect.ExtensionType]*ExtensionInfo
Joe Tsai62517cc2018-12-04 14:00:01 -080020
Damien Neilf1e905b2019-08-08 15:45:59 -070021// legacyExtensionDescFromType converts a protoreflect.ExtensionType to an
22// ExtensionInfo. The returned ExtensionInfo must not be mutated.
23func legacyExtensionDescFromType(xt pref.ExtensionType) *ExtensionInfo {
24 // Fast-path: check whether this is an ExtensionInfo.
25 if xt, ok := xt.(*ExtensionInfo); ok {
26 return xt
Joe Tsaiafb455e2019-03-14 16:08:22 -070027 }
28
Joe Tsai62517cc2018-12-04 14:00:01 -080029 // Fast-path: check the cache for whether this ExtensionType has already
Damien Neilf1e905b2019-08-08 15:45:59 -070030 // been converted to an ExtensionInfo.
31 if d, ok := legacyExtensionInfoCache.Load(xt); ok {
32 return d.(*ExtensionInfo)
Joe Tsai08e00302018-11-26 22:32:06 -080033 }
34
Damien Neilf1e905b2019-08-08 15:45:59 -070035 tt := xt.GoType()
36 if xt.Descriptor().Cardinality() == pref.Repeated {
37 tt = tt.Elem().Elem()
38 }
39 xi := &ExtensionInfo{}
40 InitExtensionInfo(xi, xt.Descriptor().Descriptor(), tt)
41 xi.lazyInit() // populate legacy fields
42
43 if xi, ok := legacyExtensionInfoCache.LoadOrStore(xt, xi); ok {
44 return xi.(*ExtensionInfo)
45 }
46 return xi
47}
48
49func (xi *ExtensionInfo) initToLegacy() {
50 xd := xi.desc
Joe Tsai4fddeba2019-03-20 18:29:32 -070051 var parent piface.MessageV1
Damien Neil92f76182019-08-02 16:58:08 -070052 messageName := xd.ContainingMessage().FullName()
Joe Tsaiac31a352019-05-13 14:32:56 -070053 if mt, _ := preg.GlobalTypes.FindMessageByName(messageName); mt != nil {
Joe Tsai08e00302018-11-26 22:32:06 -080054 // Create a new parent message and unwrap it if possible.
Joe Tsai3bc7d6f2019-01-09 02:57:13 -080055 mv := mt.New().Interface()
Joe Tsai08e00302018-11-26 22:32:06 -080056 t := reflect.TypeOf(mv)
Damien Neil954bd922019-07-17 16:52:10 -070057 if mv, ok := mv.(Unwrapper); ok {
Joe Tsai08e00302018-11-26 22:32:06 -080058 t = reflect.TypeOf(mv.ProtoUnwrap())
59 }
60
61 // Check whether the message implements the legacy v1 Message interface.
62 mz := reflect.Zero(t).Interface()
Joe Tsai4fddeba2019-03-20 18:29:32 -070063 if mz, ok := mz.(piface.MessageV1); ok {
Joe Tsai08e00302018-11-26 22:32:06 -080064 parent = mz
65 }
66 }
67
68 // Determine the v1 extension type, which is unfortunately not the same as
69 // the v2 ExtensionType.GoType.
Damien Neilf1e905b2019-08-08 15:45:59 -070070 extType := xi.goType
Joe Tsai08e00302018-11-26 22:32:06 -080071 switch extType.Kind() {
72 case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
73 extType = reflect.PtrTo(extType) // T -> *T for singular scalar fields
74 case reflect.Ptr:
75 if extType.Elem().Kind() == reflect.Slice {
76 extType = extType.Elem() // *[]T -> []T for repeated fields
77 }
78 }
79
Joe Tsai2e7817f2019-08-23 12:18:57 -070080 // Reconstruct the legacy enum full name.
Joe Tsai08e00302018-11-26 22:32:06 -080081 var enumName string
Damien Neil92f76182019-08-02 16:58:08 -070082 if xd.Kind() == pref.EnumKind {
Joe Tsai2e7817f2019-08-23 12:18:57 -070083 enumName = legacyEnumName(xd.Enum())
Joe Tsai08e00302018-11-26 22:32:06 -080084 }
85
86 // Derive the proto file that the extension was declared within.
87 var filename string
Damien Neil92f76182019-08-02 16:58:08 -070088 if fd := xd.ParentFile(); fd != nil {
Joe Tsai08e00302018-11-26 22:32:06 -080089 filename = fd.Path()
90 }
91
Joe Tsai945a1702019-07-20 14:57:56 -070092 // For MessageSet extensions, the name used is the parent message.
93 name := xd.FullName()
94 if messageset.IsMessageSetExtension(xd) {
95 name = name.Parent()
96 }
97
Damien Neilf1e905b2019-08-08 15:45:59 -070098 xi.ExtendedType = parent
99 xi.ExtensionType = reflect.Zero(extType).Interface()
100 xi.Field = int32(xd.Number())
Joe Tsai945a1702019-07-20 14:57:56 -0700101 xi.Name = string(name)
Damien Neilf1e905b2019-08-08 15:45:59 -0700102 xi.Tag = ptag.Marshal(xd, enumName)
103 xi.Filename = filename
Joe Tsai08e00302018-11-26 22:32:06 -0800104}
105
Damien Neilf1e905b2019-08-08 15:45:59 -0700106// initFromLegacy initializes an ExtensionInfo from
107// the contents of the deprecated exported fields of the type.
108func (xi *ExtensionInfo) initFromLegacy() {
Joe Tsaid8881392019-06-06 13:01:53 -0700109 // Resolve enum or message dependencies.
110 var ed pref.EnumDescriptor
111 var md pref.MessageDescriptor
Damien Neilf1e905b2019-08-08 15:45:59 -0700112 t := reflect.TypeOf(xi.ExtensionType)
Joe Tsai08e00302018-11-26 22:32:06 -0800113 isOptional := t.Kind() == reflect.Ptr && t.Elem().Kind() != reflect.Struct
114 isRepeated := t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
115 if isOptional || isRepeated {
116 t = t.Elem()
117 }
Joe Tsaid8881392019-06-06 13:01:53 -0700118 switch v := reflect.Zero(t).Interface().(type) {
119 case pref.Enum:
120 ed = v.Descriptor()
121 case enumV1:
122 ed = LegacyLoadEnumDesc(t)
123 case pref.ProtoMessage:
124 md = v.ProtoReflect().Descriptor()
125 case messageV1:
126 md = LegacyLoadMessageDesc(t)
127 }
128
129 // Derive basic field information from the struct tag.
130 var evs pref.EnumValueDescriptors
131 if ed != nil {
132 evs = ed.Values()
133 }
Damien Neilf1e905b2019-08-08 15:45:59 -0700134 fd := ptag.Unmarshal(xi.Tag, t, evs).(*filedesc.Field)
Joe Tsai08e00302018-11-26 22:32:06 -0800135
136 // Construct a v2 ExtensionType.
Joe Tsaid8881392019-06-06 13:01:53 -0700137 xd := &filedesc.Extension{L2: new(filedesc.ExtensionL2)}
138 xd.L0.ParentFile = filedesc.SurrogateProto2
Damien Neilf1e905b2019-08-08 15:45:59 -0700139 xd.L0.FullName = pref.FullName(xi.Name)
140 xd.L1.Number = pref.FieldNumber(xi.Field)
Joe Tsaid8881392019-06-06 13:01:53 -0700141 xd.L2.Cardinality = fd.L1.Cardinality
142 xd.L1.Kind = fd.L1.Kind
143 xd.L2.IsPacked = fd.L1.IsPacked
144 xd.L2.Default = fd.L1.Default
Damien Neilf1e905b2019-08-08 15:45:59 -0700145 xd.L1.Extendee = Export{}.MessageDescriptorOf(xi.ExtendedType)
Joe Tsaid8881392019-06-06 13:01:53 -0700146 xd.L2.Enum = ed
147 xd.L2.Message = md
Joe Tsai945a1702019-07-20 14:57:56 -0700148
149 // Derive real extension field name for MessageSets.
150 if messageset.IsMessageSet(xd.L1.Extendee) && md.FullName() == xd.L0.FullName {
151 xd.L0.FullName = xd.L0.FullName.Append(messageset.ExtensionName)
152 }
153
Damien Neilf1e905b2019-08-08 15:45:59 -0700154 tt := reflect.TypeOf(xi.ExtensionType)
Damien Neil954bd922019-07-17 16:52:10 -0700155 if isOptional {
156 tt = tt.Elem()
157 } else if isRepeated {
158 tt = reflect.PtrTo(tt)
159 }
Damien Neilf1e905b2019-08-08 15:45:59 -0700160 xi.desc = xd
161 xi.goType = tt
Joe Tsai08e00302018-11-26 22:32:06 -0800162}