blob: c8cb3e9841dc2c4d4fc7c6796305d8723afc54cc [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
5package legacy
6
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"
14 pimpl "google.golang.org/protobuf/internal/impl"
15 ptype "google.golang.org/protobuf/internal/prototype"
Damien Neile89e6242019-05-13 23:55:40 -070016 pvalue "google.golang.org/protobuf/internal/value"
17 pref "google.golang.org/protobuf/reflect/protoreflect"
18 preg "google.golang.org/protobuf/reflect/protoregistry"
Joe Tsaib2f66be2019-05-22 00:42:45 -040019 "google.golang.org/protobuf/reflect/prototype"
Damien Neile89e6242019-05-13 23:55:40 -070020 piface "google.golang.org/protobuf/runtime/protoiface"
Joe Tsai08e00302018-11-26 22:32:06 -080021)
22
Joe Tsai4fddeba2019-03-20 18:29:32 -070023// extensionDescKey is a comparable version of protoiface.ExtensionDescV1
Joe Tsai62517cc2018-12-04 14:00:01 -080024// suitable for use as a key in a map.
25type extensionDescKey struct {
26 typeV2 pref.ExtensionType
27 extendedType reflect.Type
28 extensionType reflect.Type
29 field int32
30 name string
31 tag string
32 filename string
33}
34
Joe Tsai4fddeba2019-03-20 18:29:32 -070035func extensionDescKeyOf(d *piface.ExtensionDescV1) extensionDescKey {
Joe Tsai62517cc2018-12-04 14:00:01 -080036 return extensionDescKey{
37 d.Type,
38 reflect.TypeOf(d.ExtendedType),
39 reflect.TypeOf(d.ExtensionType),
40 d.Field, d.Name, d.Tag, d.Filename,
41 }
42}
43
44var (
45 extensionTypeCache sync.Map // map[extensionDescKey]protoreflect.ExtensionType
Joe Tsai4fddeba2019-03-20 18:29:32 -070046 extensionDescCache sync.Map // map[protoreflect.ExtensionType]*protoiface.ExtensionDescV1
Joe Tsai62517cc2018-12-04 14:00:01 -080047)
48
Joe Tsai6dbffb72018-12-04 14:06:19 -080049// extensionDescFromType converts a v2 protoreflect.ExtensionType to a
Joe Tsai4fddeba2019-03-20 18:29:32 -070050// protoiface.ExtensionDescV1. The returned ExtensionDesc must not be mutated.
Joe Tsai0fc49f82019-05-01 12:29:25 -070051func extensionDescFromType(xt pref.ExtensionType) *piface.ExtensionDescV1 {
Joe Tsaiafb455e2019-03-14 16:08:22 -070052 // Fast-path: check whether an extension desc is already nested within.
Joe Tsai0fc49f82019-05-01 12:29:25 -070053 if xt, ok := xt.(interface {
Joe Tsai4fddeba2019-03-20 18:29:32 -070054 ProtoLegacyExtensionDesc() *piface.ExtensionDescV1
55 }); ok {
Joe Tsai0fc49f82019-05-01 12:29:25 -070056 if d := xt.ProtoLegacyExtensionDesc(); d != nil {
Joe Tsaiafb455e2019-03-14 16:08:22 -070057 return d
58 }
59 }
60
Joe Tsai62517cc2018-12-04 14:00:01 -080061 // Fast-path: check the cache for whether this ExtensionType has already
62 // been converted to a legacy descriptor.
Joe Tsai0fc49f82019-05-01 12:29:25 -070063 if d, ok := extensionDescCache.Load(xt); ok {
Joe Tsai4fddeba2019-03-20 18:29:32 -070064 return d.(*piface.ExtensionDescV1)
Joe Tsai08e00302018-11-26 22:32:06 -080065 }
66
67 // Determine the parent type if possible.
Joe Tsai4fddeba2019-03-20 18:29:32 -070068 var parent piface.MessageV1
Joe Tsaiac31a352019-05-13 14:32:56 -070069 messageName := xt.Descriptor().ContainingMessage().FullName()
70 if mt, _ := preg.GlobalTypes.FindMessageByName(messageName); mt != nil {
Joe Tsai08e00302018-11-26 22:32:06 -080071 // Create a new parent message and unwrap it if possible.
Joe Tsai3bc7d6f2019-01-09 02:57:13 -080072 mv := mt.New().Interface()
Joe Tsai08e00302018-11-26 22:32:06 -080073 t := reflect.TypeOf(mv)
74 if mv, ok := mv.(pvalue.Unwrapper); ok {
75 t = reflect.TypeOf(mv.ProtoUnwrap())
76 }
77
78 // Check whether the message implements the legacy v1 Message interface.
79 mz := reflect.Zero(t).Interface()
Joe Tsai4fddeba2019-03-20 18:29:32 -070080 if mz, ok := mz.(piface.MessageV1); ok {
Joe Tsai08e00302018-11-26 22:32:06 -080081 parent = mz
82 }
83 }
84
85 // Determine the v1 extension type, which is unfortunately not the same as
86 // the v2 ExtensionType.GoType.
Joe Tsai0fc49f82019-05-01 12:29:25 -070087 extType := xt.GoType()
Joe Tsai08e00302018-11-26 22:32:06 -080088 switch extType.Kind() {
89 case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
90 extType = reflect.PtrTo(extType) // T -> *T for singular scalar fields
91 case reflect.Ptr:
92 if extType.Elem().Kind() == reflect.Slice {
93 extType = extType.Elem() // *[]T -> []T for repeated fields
94 }
95 }
96
97 // Reconstruct the legacy enum full name, which is an odd mixture of the
98 // proto package name with the Go type name.
99 var enumName string
Joe Tsai0fc49f82019-05-01 12:29:25 -0700100 if xt.Descriptor().Kind() == pref.EnumKind {
Joe Tsai08e00302018-11-26 22:32:06 -0800101 // Derive Go type name.
Joe Tsai0fc49f82019-05-01 12:29:25 -0700102 t := extType
103 if t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice {
104 t = t.Elem()
Joe Tsai08e00302018-11-26 22:32:06 -0800105 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700106 enumName = t.Name()
Joe Tsai08e00302018-11-26 22:32:06 -0800107
108 // Derive the proto package name.
109 // For legacy enums, obtain the proto package from the raw descriptor.
110 var protoPkg string
Joe Tsai67c1d9b2019-05-12 02:27:46 -0700111 if fd := xt.Descriptor().Enum().ParentFile(); fd != nil {
Joe Tsai08e00302018-11-26 22:32:06 -0800112 protoPkg = string(fd.Package())
113 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700114 if ed, ok := reflect.Zero(t).Interface().(enumV1); ok && protoPkg == "" {
Joe Tsai08e00302018-11-26 22:32:06 -0800115 b, _ := ed.EnumDescriptor()
Damien Neil987d5702019-04-10 11:06:53 -0700116 protoPkg = loadFileDesc(b).GetPackage()
Joe Tsai08e00302018-11-26 22:32:06 -0800117 }
118
119 if protoPkg != "" {
120 enumName = protoPkg + "." + enumName
121 }
122 }
123
124 // Derive the proto file that the extension was declared within.
125 var filename string
Joe Tsai67c1d9b2019-05-12 02:27:46 -0700126 if fd := xt.Descriptor().ParentFile(); fd != nil {
Joe Tsai08e00302018-11-26 22:32:06 -0800127 filename = fd.Path()
128 }
129
Joe Tsai4fddeba2019-03-20 18:29:32 -0700130 // Construct and return a ExtensionDescV1.
131 d := &piface.ExtensionDescV1{
Joe Tsai0fc49f82019-05-01 12:29:25 -0700132 Type: xt,
Joe Tsai08e00302018-11-26 22:32:06 -0800133 ExtendedType: parent,
134 ExtensionType: reflect.Zero(extType).Interface(),
Joe Tsai0fc49f82019-05-01 12:29:25 -0700135 Field: int32(xt.Descriptor().Number()),
136 Name: string(xt.Descriptor().FullName()),
137 Tag: ptag.Marshal(xt.Descriptor(), enumName),
Joe Tsai08e00302018-11-26 22:32:06 -0800138 Filename: filename,
139 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700140 if d, ok := extensionDescCache.LoadOrStore(xt, d); ok {
Joe Tsai4fddeba2019-03-20 18:29:32 -0700141 return d.(*piface.ExtensionDescV1)
Joe Tsaib9365042019-03-19 14:14:29 -0700142 }
Joe Tsai62517cc2018-12-04 14:00:01 -0800143 return d
Joe Tsai08e00302018-11-26 22:32:06 -0800144}
145
Joe Tsai4fddeba2019-03-20 18:29:32 -0700146// extensionTypeFromDesc converts a protoiface.ExtensionDescV1 to a
Joe Tsai62517cc2018-12-04 14:00:01 -0800147// v2 protoreflect.ExtensionType. The returned descriptor type takes ownership
148// of the input extension desc. The input must not be mutated so long as the
149// returned type is still in use.
Joe Tsai4fddeba2019-03-20 18:29:32 -0700150func extensionTypeFromDesc(d *piface.ExtensionDescV1) pref.ExtensionType {
Joe Tsai62517cc2018-12-04 14:00:01 -0800151 // Fast-path: check whether an extension type is already nested within.
Joe Tsai08e00302018-11-26 22:32:06 -0800152 if d.Type != nil {
Joe Tsai62517cc2018-12-04 14:00:01 -0800153 return d.Type
154 }
155
156 // Fast-path: check the cache for whether this ExtensionType has already
157 // been converted from a legacy descriptor.
158 dk := extensionDescKeyOf(d)
159 if t, ok := extensionTypeCache.Load(dk); ok {
160 return t.(pref.ExtensionType)
Joe Tsai08e00302018-11-26 22:32:06 -0800161 }
162
163 // Derive basic field information from the struct tag.
164 t := reflect.TypeOf(d.ExtensionType)
165 isOptional := t.Kind() == reflect.Ptr && t.Elem().Kind() != reflect.Struct
166 isRepeated := t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
167 if isOptional || isRepeated {
168 t = t.Elem()
169 }
170 f := ptag.Unmarshal(d.Tag, t)
171
172 // Construct a v2 ExtensionType.
Joe Tsai0fc49f82019-05-01 12:29:25 -0700173 var ed pref.EnumDescriptor
174 var md pref.MessageDescriptor
Joe Tsaib2f66be2019-05-22 00:42:45 -0400175 switch f.Kind {
176 case pref.EnumKind:
177 if e, ok := reflect.Zero(t).Interface().(pref.Enum); ok {
178 ed = e.Descriptor()
179 } else {
180 ed = LoadEnumDesc(t)
181 }
182 case pref.MessageKind, pref.GroupKind:
183 if m, ok := reflect.Zero(t).Interface().(pref.ProtoMessage); ok {
184 md = m.ProtoReflect().Descriptor()
185 } else {
186 md = LoadMessageDesc(t)
187 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700188 }
Joe Tsai08e00302018-11-26 22:32:06 -0800189 xd, err := ptype.NewExtension(&ptype.StandaloneExtension{
190 FullName: pref.FullName(d.Name),
191 Number: pref.FieldNumber(d.Field),
192 Cardinality: f.Cardinality,
193 Kind: f.Kind,
194 Default: f.Default,
195 Options: f.Options,
Joe Tsai0fc49f82019-05-01 12:29:25 -0700196 EnumType: ed,
197 MessageType: md,
198 ExtendedType: pimpl.Export{}.MessageDescriptorOf(d.ExtendedType),
Joe Tsai08e00302018-11-26 22:32:06 -0800199 })
200 if err != nil {
201 panic(err)
202 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700203 xt := ExtensionTypeOf(xd, t)
Joe Tsai08e00302018-11-26 22:32:06 -0800204
Joe Tsai62517cc2018-12-04 14:00:01 -0800205 // Cache the conversion for both directions.
Joe Tsaib9365042019-03-19 14:14:29 -0700206 extensionDescCache.LoadOrStore(xt, d)
207 if xt, ok := extensionTypeCache.LoadOrStore(dk, xt); ok {
208 return xt.(pref.ExtensionType)
209 }
Joe Tsai62517cc2018-12-04 14:00:01 -0800210 return xt
Joe Tsai08e00302018-11-26 22:32:06 -0800211}
212
Joe Tsai0fc49f82019-05-01 12:29:25 -0700213// ExtensionTypeOf returns a protoreflect.ExtensionType where the type of the
214// field is t. The type t must be provided if the field is an enum or message.
Joe Tsai08e00302018-11-26 22:32:06 -0800215//
Joe Tsai0fc49f82019-05-01 12:29:25 -0700216// This is exported for testing purposes.
217func ExtensionTypeOf(xd pref.ExtensionDescriptor, t reflect.Type) pref.ExtensionType {
Joe Tsaib2f66be2019-05-22 00:42:45 -0400218 var conv pvalue.Converter
219 var isLegacy bool
220 xt := &prototype.Extension{ExtensionDescriptor: xd}
Joe Tsai0fc49f82019-05-01 12:29:25 -0700221 switch xd.Kind() {
Joe Tsaib2f66be2019-05-22 00:42:45 -0400222 case pref.EnumKind:
223 conv, isLegacy = newConverter(t, xd.Kind())
224 xt.NewEnum = conv.NewEnum
225 case pref.MessageKind, pref.GroupKind:
226 conv, isLegacy = newConverter(t, xd.Kind())
227 xt.NewMessage = conv.NewMessage
Joe Tsai0fc49f82019-05-01 12:29:25 -0700228 default:
Joe Tsaib2f66be2019-05-22 00:42:45 -0400229 // Extension types for non-enums and non-messages are simple.
230 return &prototype.Extension{ExtensionDescriptor: xd}
Joe Tsai0fc49f82019-05-01 12:29:25 -0700231 }
Joe Tsaib2f66be2019-05-22 00:42:45 -0400232 if !isLegacy {
Joe Tsai0fc49f82019-05-01 12:29:25 -0700233 return xt
234 }
Joe Tsai08e00302018-11-26 22:32:06 -0800235
Joe Tsai0fc49f82019-05-01 12:29:25 -0700236 // Wrap ExtensionType such that GoType presents the legacy Go type.
Joe Tsai6dbffb72018-12-04 14:06:19 -0800237 xt2 := &extensionType{ExtensionType: xt}
Joe Tsai08e00302018-11-26 22:32:06 -0800238 if xd.Cardinality() != pref.Repeated {
239 xt2.typ = t
Joe Tsaid18bd312019-01-09 03:23:55 -0800240 xt2.new = func() pref.Value {
241 return xt.New()
Joe Tsai08e00302018-11-26 22:32:06 -0800242 }
243 xt2.valueOf = func(v interface{}) pref.Value {
244 if reflect.TypeOf(v) != xt2.typ {
245 panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
246 }
247 if xd.Kind() == pref.EnumKind {
248 return xt.ValueOf(Export{}.EnumOf(v))
249 } else {
Joe Tsai22b1ebd2019-03-11 13:45:14 -0700250 return xt.ValueOf(Export{}.MessageOf(v).Interface())
Joe Tsai08e00302018-11-26 22:32:06 -0800251 }
252 }
253 xt2.interfaceOf = func(v pref.Value) interface{} {
254 return xt.InterfaceOf(v).(pvalue.Unwrapper).ProtoUnwrap()
255 }
256 } else {
257 xt2.typ = reflect.PtrTo(reflect.SliceOf(t))
Joe Tsaid18bd312019-01-09 03:23:55 -0800258 xt2.new = func() pref.Value {
259 v := reflect.New(xt2.typ.Elem()).Interface()
260 return pref.ValueOf(pvalue.ListOf(v, conv))
Joe Tsai08e00302018-11-26 22:32:06 -0800261 }
262 xt2.valueOf = func(v interface{}) pref.Value {
263 if reflect.TypeOf(v) != xt2.typ {
264 panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
265 }
266 return pref.ValueOf(pvalue.ListOf(v, conv))
267 }
268 xt2.interfaceOf = func(pv pref.Value) interface{} {
269 v := pv.List().(pvalue.Unwrapper).ProtoUnwrap()
270 if reflect.TypeOf(v) != xt2.typ {
271 panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
272 }
273 return v
274 }
275 }
276 return xt2
277}
278
Joe Tsai6dbffb72018-12-04 14:06:19 -0800279type extensionType struct {
Joe Tsai08e00302018-11-26 22:32:06 -0800280 pref.ExtensionType
281 typ reflect.Type
Joe Tsaid18bd312019-01-09 03:23:55 -0800282 new func() pref.Value
Joe Tsai08e00302018-11-26 22:32:06 -0800283 valueOf func(interface{}) pref.Value
284 interfaceOf func(pref.Value) interface{}
285}
286
Joe Tsai6dbffb72018-12-04 14:06:19 -0800287func (x *extensionType) GoType() reflect.Type { return x.typ }
Joe Tsaid18bd312019-01-09 03:23:55 -0800288func (x *extensionType) New() pref.Value { return x.new() }
Joe Tsai6dbffb72018-12-04 14:06:19 -0800289func (x *extensionType) ValueOf(v interface{}) pref.Value { return x.valueOf(v) }
290func (x *extensionType) InterfaceOf(v pref.Value) interface{} { return x.interfaceOf(v) }
Joe Tsai63bdd1f2019-05-22 05:22:04 -0400291func (x *extensionType) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, x.Descriptor()) }