blob: c8b13d8f637435a18ca0b2c9c1c51b3ce2c12550 [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 (
8 "fmt"
9 "reflect"
Joe Tsai62517cc2018-12-04 14:00:01 -080010 "sync"
Joe Tsai08e00302018-11-26 22:32:06 -080011
Joe Tsai63bdd1f2019-05-22 05:22:04 -040012 "google.golang.org/protobuf/internal/descfmt"
Damien Neile89e6242019-05-13 23:55:40 -070013 ptag "google.golang.org/protobuf/internal/encoding/tag"
Joe Tsaid8881392019-06-06 13:01:53 -070014 "google.golang.org/protobuf/internal/filedesc"
Damien Neile89e6242019-05-13 23:55:40 -070015 pvalue "google.golang.org/protobuf/internal/value"
16 pref "google.golang.org/protobuf/reflect/protoreflect"
17 preg "google.golang.org/protobuf/reflect/protoregistry"
Joe Tsaib2f66be2019-05-22 00:42:45 -040018 "google.golang.org/protobuf/reflect/prototype"
Damien Neile89e6242019-05-13 23:55:40 -070019 piface "google.golang.org/protobuf/runtime/protoiface"
Joe Tsai08e00302018-11-26 22:32:06 -080020)
21
Joe Tsai21ade492019-05-22 13:42:54 -040022// legacyExtensionDescKey is a comparable version of protoiface.ExtensionDescV1
Joe Tsai62517cc2018-12-04 14:00:01 -080023// suitable for use as a key in a map.
Joe Tsai21ade492019-05-22 13:42:54 -040024type legacyExtensionDescKey struct {
Joe Tsai62517cc2018-12-04 14:00:01 -080025 typeV2 pref.ExtensionType
26 extendedType reflect.Type
27 extensionType reflect.Type
28 field int32
29 name string
30 tag string
31 filename string
32}
33
Joe Tsai21ade492019-05-22 13:42:54 -040034func legacyExtensionDescKeyOf(d *piface.ExtensionDescV1) legacyExtensionDescKey {
35 return legacyExtensionDescKey{
Joe Tsai62517cc2018-12-04 14:00:01 -080036 d.Type,
37 reflect.TypeOf(d.ExtendedType),
38 reflect.TypeOf(d.ExtensionType),
39 d.Field, d.Name, d.Tag, d.Filename,
40 }
41}
42
43var (
Joe Tsai21ade492019-05-22 13:42:54 -040044 legacyExtensionTypeCache sync.Map // map[legacyExtensionDescKey]protoreflect.ExtensionType
45 legacyExtensionDescCache sync.Map // map[protoreflect.ExtensionType]*protoiface.ExtensionDescV1
Joe Tsai62517cc2018-12-04 14:00:01 -080046)
47
Joe Tsai21ade492019-05-22 13:42:54 -040048// legacyExtensionDescFromType converts a v2 protoreflect.ExtensionType to a
Joe Tsai4fddeba2019-03-20 18:29:32 -070049// protoiface.ExtensionDescV1. The returned ExtensionDesc must not be mutated.
Joe Tsai21ade492019-05-22 13:42:54 -040050func legacyExtensionDescFromType(xt pref.ExtensionType) *piface.ExtensionDescV1 {
Joe Tsaiafb455e2019-03-14 16:08:22 -070051 // Fast-path: check whether an extension desc is already nested within.
Joe Tsai0fc49f82019-05-01 12:29:25 -070052 if xt, ok := xt.(interface {
Joe Tsai4fddeba2019-03-20 18:29:32 -070053 ProtoLegacyExtensionDesc() *piface.ExtensionDescV1
54 }); ok {
Joe Tsai0fc49f82019-05-01 12:29:25 -070055 if d := xt.ProtoLegacyExtensionDesc(); d != nil {
Joe Tsaiafb455e2019-03-14 16:08:22 -070056 return d
57 }
58 }
59
Joe Tsai62517cc2018-12-04 14:00:01 -080060 // Fast-path: check the cache for whether this ExtensionType has already
61 // been converted to a legacy descriptor.
Joe Tsai21ade492019-05-22 13:42:54 -040062 if d, ok := legacyExtensionDescCache.Load(xt); ok {
Joe Tsai4fddeba2019-03-20 18:29:32 -070063 return d.(*piface.ExtensionDescV1)
Joe Tsai08e00302018-11-26 22:32:06 -080064 }
65
66 // Determine the parent type if possible.
Joe Tsai4fddeba2019-03-20 18:29:32 -070067 var parent piface.MessageV1
Joe Tsaid4211502019-07-02 14:58:02 -070068 messageName := xt.ContainingMessage().FullName()
Joe Tsaiac31a352019-05-13 14:32:56 -070069 if mt, _ := preg.GlobalTypes.FindMessageByName(messageName); mt != nil {
Joe Tsai08e00302018-11-26 22:32:06 -080070 // Create a new parent message and unwrap it if possible.
Joe Tsai3bc7d6f2019-01-09 02:57:13 -080071 mv := mt.New().Interface()
Joe Tsai08e00302018-11-26 22:32:06 -080072 t := reflect.TypeOf(mv)
73 if mv, ok := mv.(pvalue.Unwrapper); ok {
74 t = reflect.TypeOf(mv.ProtoUnwrap())
75 }
76
77 // Check whether the message implements the legacy v1 Message interface.
78 mz := reflect.Zero(t).Interface()
Joe Tsai4fddeba2019-03-20 18:29:32 -070079 if mz, ok := mz.(piface.MessageV1); ok {
Joe Tsai08e00302018-11-26 22:32:06 -080080 parent = mz
81 }
82 }
83
84 // Determine the v1 extension type, which is unfortunately not the same as
85 // the v2 ExtensionType.GoType.
Joe Tsai0fc49f82019-05-01 12:29:25 -070086 extType := xt.GoType()
Joe Tsai08e00302018-11-26 22:32:06 -080087 switch extType.Kind() {
88 case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
89 extType = reflect.PtrTo(extType) // T -> *T for singular scalar fields
90 case reflect.Ptr:
91 if extType.Elem().Kind() == reflect.Slice {
92 extType = extType.Elem() // *[]T -> []T for repeated fields
93 }
94 }
95
96 // Reconstruct the legacy enum full name, which is an odd mixture of the
97 // proto package name with the Go type name.
98 var enumName string
Joe Tsaid4211502019-07-02 14:58:02 -070099 if xt.Kind() == pref.EnumKind {
Joe Tsai08e00302018-11-26 22:32:06 -0800100 // Derive Go type name.
Joe Tsai0fc49f82019-05-01 12:29:25 -0700101 t := extType
102 if t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice {
103 t = t.Elem()
Joe Tsai08e00302018-11-26 22:32:06 -0800104 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700105 enumName = t.Name()
Joe Tsai08e00302018-11-26 22:32:06 -0800106
107 // Derive the proto package name.
108 // For legacy enums, obtain the proto package from the raw descriptor.
109 var protoPkg string
Joe Tsaid4211502019-07-02 14:58:02 -0700110 if fd := xt.Enum().ParentFile(); fd != nil {
Joe Tsai08e00302018-11-26 22:32:06 -0800111 protoPkg = string(fd.Package())
112 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700113 if ed, ok := reflect.Zero(t).Interface().(enumV1); ok && protoPkg == "" {
Joe Tsai08e00302018-11-26 22:32:06 -0800114 b, _ := ed.EnumDescriptor()
Joe Tsaid8881392019-06-06 13:01:53 -0700115 protoPkg = string(legacyLoadFileDesc(b).Package())
Joe Tsai08e00302018-11-26 22:32:06 -0800116 }
117
118 if protoPkg != "" {
119 enumName = protoPkg + "." + enumName
120 }
121 }
122
123 // Derive the proto file that the extension was declared within.
124 var filename string
Joe Tsaid4211502019-07-02 14:58:02 -0700125 if fd := xt.ParentFile(); fd != nil {
Joe Tsai08e00302018-11-26 22:32:06 -0800126 filename = fd.Path()
127 }
128
Joe Tsai4fddeba2019-03-20 18:29:32 -0700129 // Construct and return a ExtensionDescV1.
130 d := &piface.ExtensionDescV1{
Joe Tsai0fc49f82019-05-01 12:29:25 -0700131 Type: xt,
Joe Tsai08e00302018-11-26 22:32:06 -0800132 ExtendedType: parent,
133 ExtensionType: reflect.Zero(extType).Interface(),
Joe Tsaid4211502019-07-02 14:58:02 -0700134 Field: int32(xt.Number()),
135 Name: string(xt.FullName()),
136 Tag: ptag.Marshal(xt, enumName),
Joe Tsai08e00302018-11-26 22:32:06 -0800137 Filename: filename,
138 }
Joe Tsai21ade492019-05-22 13:42:54 -0400139 if d, ok := legacyExtensionDescCache.LoadOrStore(xt, d); ok {
Joe Tsai4fddeba2019-03-20 18:29:32 -0700140 return d.(*piface.ExtensionDescV1)
Joe Tsaib9365042019-03-19 14:14:29 -0700141 }
Joe Tsai62517cc2018-12-04 14:00:01 -0800142 return d
Joe Tsai08e00302018-11-26 22:32:06 -0800143}
144
Joe Tsai21ade492019-05-22 13:42:54 -0400145// legacyExtensionTypeFromDesc converts a protoiface.ExtensionDescV1 to a
Joe Tsai62517cc2018-12-04 14:00:01 -0800146// v2 protoreflect.ExtensionType. The returned descriptor type takes ownership
147// of the input extension desc. The input must not be mutated so long as the
148// returned type is still in use.
Joe Tsai21ade492019-05-22 13:42:54 -0400149func legacyExtensionTypeFromDesc(d *piface.ExtensionDescV1) pref.ExtensionType {
Joe Tsai62517cc2018-12-04 14:00:01 -0800150 // Fast-path: check whether an extension type is already nested within.
Joe Tsai08e00302018-11-26 22:32:06 -0800151 if d.Type != nil {
Joe Tsai62517cc2018-12-04 14:00:01 -0800152 return d.Type
153 }
154
155 // Fast-path: check the cache for whether this ExtensionType has already
156 // been converted from a legacy descriptor.
Joe Tsai21ade492019-05-22 13:42:54 -0400157 dk := legacyExtensionDescKeyOf(d)
158 if t, ok := legacyExtensionTypeCache.Load(dk); ok {
Joe Tsai62517cc2018-12-04 14:00:01 -0800159 return t.(pref.ExtensionType)
Joe Tsai08e00302018-11-26 22:32:06 -0800160 }
161
Joe Tsaid8881392019-06-06 13:01:53 -0700162 // Resolve enum or message dependencies.
163 var ed pref.EnumDescriptor
164 var md pref.MessageDescriptor
Joe Tsai08e00302018-11-26 22:32:06 -0800165 t := reflect.TypeOf(d.ExtensionType)
166 isOptional := t.Kind() == reflect.Ptr && t.Elem().Kind() != reflect.Struct
167 isRepeated := t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
168 if isOptional || isRepeated {
169 t = t.Elem()
170 }
Joe Tsaid8881392019-06-06 13:01:53 -0700171 switch v := reflect.Zero(t).Interface().(type) {
172 case pref.Enum:
173 ed = v.Descriptor()
174 case enumV1:
175 ed = LegacyLoadEnumDesc(t)
176 case pref.ProtoMessage:
177 md = v.ProtoReflect().Descriptor()
178 case messageV1:
179 md = LegacyLoadMessageDesc(t)
180 }
181
182 // Derive basic field information from the struct tag.
183 var evs pref.EnumValueDescriptors
184 if ed != nil {
185 evs = ed.Values()
186 }
187 fd := ptag.Unmarshal(d.Tag, t, evs).(*filedesc.Field)
Joe Tsai08e00302018-11-26 22:32:06 -0800188
189 // Construct a v2 ExtensionType.
Joe Tsaid8881392019-06-06 13:01:53 -0700190 xd := &filedesc.Extension{L2: new(filedesc.ExtensionL2)}
191 xd.L0.ParentFile = filedesc.SurrogateProto2
192 xd.L0.FullName = pref.FullName(d.Name)
193 xd.L1.Number = pref.FieldNumber(d.Field)
194 xd.L2.Cardinality = fd.L1.Cardinality
195 xd.L1.Kind = fd.L1.Kind
196 xd.L2.IsPacked = fd.L1.IsPacked
197 xd.L2.Default = fd.L1.Default
198 xd.L1.Extendee = Export{}.MessageDescriptorOf(d.ExtendedType)
199 xd.L2.Enum = ed
200 xd.L2.Message = md
Joe Tsai21ade492019-05-22 13:42:54 -0400201 xt := LegacyExtensionTypeOf(xd, t)
Joe Tsai08e00302018-11-26 22:32:06 -0800202
Joe Tsai62517cc2018-12-04 14:00:01 -0800203 // Cache the conversion for both directions.
Joe Tsai21ade492019-05-22 13:42:54 -0400204 legacyExtensionDescCache.LoadOrStore(xt, d)
205 if xt, ok := legacyExtensionTypeCache.LoadOrStore(dk, xt); ok {
Joe Tsaib9365042019-03-19 14:14:29 -0700206 return xt.(pref.ExtensionType)
207 }
Joe Tsai62517cc2018-12-04 14:00:01 -0800208 return xt
Joe Tsai08e00302018-11-26 22:32:06 -0800209}
210
Joe Tsai21ade492019-05-22 13:42:54 -0400211// LegacyExtensionTypeOf returns a protoreflect.ExtensionType where the
212// element type of the field is t. The type t must be provided if the field
213// is an enum or message.
Joe Tsai08e00302018-11-26 22:32:06 -0800214//
Joe Tsai0fc49f82019-05-01 12:29:25 -0700215// This is exported for testing purposes.
Joe Tsai21ade492019-05-22 13:42:54 -0400216func LegacyExtensionTypeOf(xd pref.ExtensionDescriptor, t reflect.Type) pref.ExtensionType {
Joe Tsaib2f66be2019-05-22 00:42:45 -0400217 var conv pvalue.Converter
218 var isLegacy bool
219 xt := &prototype.Extension{ExtensionDescriptor: xd}
Joe Tsai0fc49f82019-05-01 12:29:25 -0700220 switch xd.Kind() {
Joe Tsaib2f66be2019-05-22 00:42:45 -0400221 case pref.EnumKind:
222 conv, isLegacy = newConverter(t, xd.Kind())
223 xt.NewEnum = conv.NewEnum
224 case pref.MessageKind, pref.GroupKind:
225 conv, isLegacy = newConverter(t, xd.Kind())
226 xt.NewMessage = conv.NewMessage
Joe Tsai0fc49f82019-05-01 12:29:25 -0700227 default:
Joe Tsaib2f66be2019-05-22 00:42:45 -0400228 // Extension types for non-enums and non-messages are simple.
229 return &prototype.Extension{ExtensionDescriptor: xd}
Joe Tsai0fc49f82019-05-01 12:29:25 -0700230 }
Joe Tsaib2f66be2019-05-22 00:42:45 -0400231 if !isLegacy {
Joe Tsai0fc49f82019-05-01 12:29:25 -0700232 return xt
233 }
Joe Tsai08e00302018-11-26 22:32:06 -0800234
Joe Tsai0fc49f82019-05-01 12:29:25 -0700235 // Wrap ExtensionType such that GoType presents the legacy Go type.
Joe Tsai21ade492019-05-22 13:42:54 -0400236 xt2 := &legacyExtensionType{ExtensionType: xt}
Joe Tsai08e00302018-11-26 22:32:06 -0800237 if xd.Cardinality() != pref.Repeated {
238 xt2.typ = t
Joe Tsaid18bd312019-01-09 03:23:55 -0800239 xt2.new = func() pref.Value {
240 return xt.New()
Joe Tsai08e00302018-11-26 22:32:06 -0800241 }
242 xt2.valueOf = func(v interface{}) pref.Value {
243 if reflect.TypeOf(v) != xt2.typ {
244 panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
245 }
246 if xd.Kind() == pref.EnumKind {
247 return xt.ValueOf(Export{}.EnumOf(v))
248 } else {
Joe Tsai22b1ebd2019-03-11 13:45:14 -0700249 return xt.ValueOf(Export{}.MessageOf(v).Interface())
Joe Tsai08e00302018-11-26 22:32:06 -0800250 }
251 }
252 xt2.interfaceOf = func(v pref.Value) interface{} {
253 return xt.InterfaceOf(v).(pvalue.Unwrapper).ProtoUnwrap()
254 }
255 } else {
256 xt2.typ = reflect.PtrTo(reflect.SliceOf(t))
Joe Tsaid18bd312019-01-09 03:23:55 -0800257 xt2.new = func() pref.Value {
258 v := reflect.New(xt2.typ.Elem()).Interface()
259 return pref.ValueOf(pvalue.ListOf(v, conv))
Joe Tsai08e00302018-11-26 22:32:06 -0800260 }
261 xt2.valueOf = func(v interface{}) pref.Value {
262 if reflect.TypeOf(v) != xt2.typ {
263 panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
264 }
265 return pref.ValueOf(pvalue.ListOf(v, conv))
266 }
267 xt2.interfaceOf = func(pv pref.Value) interface{} {
268 v := pv.List().(pvalue.Unwrapper).ProtoUnwrap()
269 if reflect.TypeOf(v) != xt2.typ {
270 panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
271 }
272 return v
273 }
274 }
275 return xt2
276}
277
Joe Tsai21ade492019-05-22 13:42:54 -0400278type legacyExtensionType struct {
Joe Tsai08e00302018-11-26 22:32:06 -0800279 pref.ExtensionType
280 typ reflect.Type
Joe Tsaid18bd312019-01-09 03:23:55 -0800281 new func() pref.Value
Joe Tsai08e00302018-11-26 22:32:06 -0800282 valueOf func(interface{}) pref.Value
283 interfaceOf func(pref.Value) interface{}
284}
285
Joe Tsai21ade492019-05-22 13:42:54 -0400286func (x *legacyExtensionType) GoType() reflect.Type { return x.typ }
287func (x *legacyExtensionType) New() pref.Value { return x.new() }
288func (x *legacyExtensionType) ValueOf(v interface{}) pref.Value { return x.valueOf(v) }
289func (x *legacyExtensionType) InterfaceOf(v pref.Value) interface{} { return x.interfaceOf(v) }
Joe Tsaid4211502019-07-02 14:58:02 -0700290func (x *legacyExtensionType) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, x) }