blob: 91745550a03a7753498c42898e796fcd0e8aa61a [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 Tsai08e00302018-11-26 22:32:06 -080012 ptag "github.com/golang/protobuf/v2/internal/encoding/tag"
13 pimpl "github.com/golang/protobuf/v2/internal/impl"
Joe Tsai990b9f52019-03-13 12:56:39 -070014 ptype "github.com/golang/protobuf/v2/internal/prototype"
Joe Tsaib3960a72018-12-05 10:35:35 -080015 pfmt "github.com/golang/protobuf/v2/internal/typefmt"
Joe Tsai08e00302018-11-26 22:32:06 -080016 pvalue "github.com/golang/protobuf/v2/internal/value"
17 pref "github.com/golang/protobuf/v2/reflect/protoreflect"
Joe Tsai0fc49f82019-05-01 12:29:25 -070018 preg "github.com/golang/protobuf/v2/reflect/protoregistry"
Joe Tsai4fddeba2019-03-20 18:29:32 -070019 piface "github.com/golang/protobuf/v2/runtime/protoiface"
Joe Tsai08e00302018-11-26 22:32:06 -080020)
21
Joe Tsai4fddeba2019-03-20 18:29:32 -070022// extensionDescKey is a comparable version of protoiface.ExtensionDescV1
Joe Tsai62517cc2018-12-04 14:00:01 -080023// suitable for use as a key in a map.
24type extensionDescKey struct {
25 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 Tsai4fddeba2019-03-20 18:29:32 -070034func extensionDescKeyOf(d *piface.ExtensionDescV1) extensionDescKey {
Joe Tsai62517cc2018-12-04 14:00:01 -080035 return extensionDescKey{
36 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 (
44 extensionTypeCache sync.Map // map[extensionDescKey]protoreflect.ExtensionType
Joe Tsai4fddeba2019-03-20 18:29:32 -070045 extensionDescCache sync.Map // map[protoreflect.ExtensionType]*protoiface.ExtensionDescV1
Joe Tsai62517cc2018-12-04 14:00:01 -080046)
47
Joe Tsai6dbffb72018-12-04 14:06:19 -080048// extensionDescFromType 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 Tsai0fc49f82019-05-01 12:29:25 -070050func extensionDescFromType(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 Tsai0fc49f82019-05-01 12:29:25 -070062 if d, ok := extensionDescCache.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 Tsai0fc49f82019-05-01 12:29:25 -070068 if mt, _ := preg.GlobalTypes.FindMessageByName(xt.Descriptor().Extendee().FullName()); mt != nil {
Joe Tsai08e00302018-11-26 22:32:06 -080069 // Create a new parent message and unwrap it if possible.
Joe Tsai3bc7d6f2019-01-09 02:57:13 -080070 mv := mt.New().Interface()
Joe Tsai08e00302018-11-26 22:32:06 -080071 t := reflect.TypeOf(mv)
72 if mv, ok := mv.(pvalue.Unwrapper); ok {
73 t = reflect.TypeOf(mv.ProtoUnwrap())
74 }
75
76 // Check whether the message implements the legacy v1 Message interface.
77 mz := reflect.Zero(t).Interface()
Joe Tsai4fddeba2019-03-20 18:29:32 -070078 if mz, ok := mz.(piface.MessageV1); ok {
Joe Tsai08e00302018-11-26 22:32:06 -080079 parent = mz
80 }
81 }
82
83 // Determine the v1 extension type, which is unfortunately not the same as
84 // the v2 ExtensionType.GoType.
Joe Tsai0fc49f82019-05-01 12:29:25 -070085 extType := xt.GoType()
Joe Tsai08e00302018-11-26 22:32:06 -080086 switch extType.Kind() {
87 case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
88 extType = reflect.PtrTo(extType) // T -> *T for singular scalar fields
89 case reflect.Ptr:
90 if extType.Elem().Kind() == reflect.Slice {
91 extType = extType.Elem() // *[]T -> []T for repeated fields
92 }
93 }
94
95 // Reconstruct the legacy enum full name, which is an odd mixture of the
96 // proto package name with the Go type name.
97 var enumName string
Joe Tsai0fc49f82019-05-01 12:29:25 -070098 if xt.Descriptor().Kind() == pref.EnumKind {
Joe Tsai08e00302018-11-26 22:32:06 -080099 // Derive Go type name.
Joe Tsai0fc49f82019-05-01 12:29:25 -0700100 t := extType
101 if t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice {
102 t = t.Elem()
Joe Tsai08e00302018-11-26 22:32:06 -0800103 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700104 enumName = t.Name()
Joe Tsai08e00302018-11-26 22:32:06 -0800105
106 // Derive the proto package name.
107 // For legacy enums, obtain the proto package from the raw descriptor.
108 var protoPkg string
Joe Tsai67c1d9b2019-05-12 02:27:46 -0700109 if fd := xt.Descriptor().Enum().ParentFile(); fd != nil {
Joe Tsai08e00302018-11-26 22:32:06 -0800110 protoPkg = string(fd.Package())
111 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700112 if ed, ok := reflect.Zero(t).Interface().(enumV1); ok && protoPkg == "" {
Joe Tsai08e00302018-11-26 22:32:06 -0800113 b, _ := ed.EnumDescriptor()
Damien Neil987d5702019-04-10 11:06:53 -0700114 protoPkg = loadFileDesc(b).GetPackage()
Joe Tsai08e00302018-11-26 22:32:06 -0800115 }
116
117 if protoPkg != "" {
118 enumName = protoPkg + "." + enumName
119 }
120 }
121
122 // Derive the proto file that the extension was declared within.
123 var filename string
Joe Tsai67c1d9b2019-05-12 02:27:46 -0700124 if fd := xt.Descriptor().ParentFile(); fd != nil {
Joe Tsai08e00302018-11-26 22:32:06 -0800125 filename = fd.Path()
126 }
127
Joe Tsai4fddeba2019-03-20 18:29:32 -0700128 // Construct and return a ExtensionDescV1.
129 d := &piface.ExtensionDescV1{
Joe Tsai0fc49f82019-05-01 12:29:25 -0700130 Type: xt,
Joe Tsai08e00302018-11-26 22:32:06 -0800131 ExtendedType: parent,
132 ExtensionType: reflect.Zero(extType).Interface(),
Joe Tsai0fc49f82019-05-01 12:29:25 -0700133 Field: int32(xt.Descriptor().Number()),
134 Name: string(xt.Descriptor().FullName()),
135 Tag: ptag.Marshal(xt.Descriptor(), enumName),
Joe Tsai08e00302018-11-26 22:32:06 -0800136 Filename: filename,
137 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700138 if d, ok := extensionDescCache.LoadOrStore(xt, d); ok {
Joe Tsai4fddeba2019-03-20 18:29:32 -0700139 return d.(*piface.ExtensionDescV1)
Joe Tsaib9365042019-03-19 14:14:29 -0700140 }
Joe Tsai62517cc2018-12-04 14:00:01 -0800141 return d
Joe Tsai08e00302018-11-26 22:32:06 -0800142}
143
Joe Tsai4fddeba2019-03-20 18:29:32 -0700144// extensionTypeFromDesc converts a protoiface.ExtensionDescV1 to a
Joe Tsai62517cc2018-12-04 14:00:01 -0800145// v2 protoreflect.ExtensionType. The returned descriptor type takes ownership
146// of the input extension desc. The input must not be mutated so long as the
147// returned type is still in use.
Joe Tsai4fddeba2019-03-20 18:29:32 -0700148func extensionTypeFromDesc(d *piface.ExtensionDescV1) pref.ExtensionType {
Joe Tsai62517cc2018-12-04 14:00:01 -0800149 // Fast-path: check whether an extension type is already nested within.
Joe Tsai08e00302018-11-26 22:32:06 -0800150 if d.Type != nil {
Joe Tsai62517cc2018-12-04 14:00:01 -0800151 return d.Type
152 }
153
154 // Fast-path: check the cache for whether this ExtensionType has already
155 // been converted from a legacy descriptor.
156 dk := extensionDescKeyOf(d)
157 if t, ok := extensionTypeCache.Load(dk); ok {
158 return t.(pref.ExtensionType)
Joe Tsai08e00302018-11-26 22:32:06 -0800159 }
160
161 // Derive basic field information from the struct tag.
162 t := reflect.TypeOf(d.ExtensionType)
163 isOptional := t.Kind() == reflect.Ptr && t.Elem().Kind() != reflect.Struct
164 isRepeated := t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
165 if isOptional || isRepeated {
166 t = t.Elem()
167 }
168 f := ptag.Unmarshal(d.Tag, t)
169
170 // Construct a v2 ExtensionType.
Joe Tsai0fc49f82019-05-01 12:29:25 -0700171 var ed pref.EnumDescriptor
172 var md pref.MessageDescriptor
Joe Tsai08e00302018-11-26 22:32:06 -0800173 conv := pvalue.NewLegacyConverter(t, f.Kind, Export{})
Joe Tsai0fc49f82019-05-01 12:29:25 -0700174 if conv.EnumType != nil {
175 ed = conv.EnumType.Descriptor()
176 }
177 if conv.MessageType != nil {
178 md = conv.MessageType.Descriptor()
179 }
Joe Tsai08e00302018-11-26 22:32:06 -0800180 xd, err := ptype.NewExtension(&ptype.StandaloneExtension{
181 FullName: pref.FullName(d.Name),
182 Number: pref.FieldNumber(d.Field),
183 Cardinality: f.Cardinality,
184 Kind: f.Kind,
185 Default: f.Default,
186 Options: f.Options,
Joe Tsai0fc49f82019-05-01 12:29:25 -0700187 EnumType: ed,
188 MessageType: md,
189 ExtendedType: pimpl.Export{}.MessageDescriptorOf(d.ExtendedType),
Joe Tsai08e00302018-11-26 22:32:06 -0800190 })
191 if err != nil {
192 panic(err)
193 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700194 xt := ExtensionTypeOf(xd, t)
Joe Tsai08e00302018-11-26 22:32:06 -0800195
Joe Tsai62517cc2018-12-04 14:00:01 -0800196 // Cache the conversion for both directions.
Joe Tsaib9365042019-03-19 14:14:29 -0700197 extensionDescCache.LoadOrStore(xt, d)
198 if xt, ok := extensionTypeCache.LoadOrStore(dk, xt); ok {
199 return xt.(pref.ExtensionType)
200 }
Joe Tsai62517cc2018-12-04 14:00:01 -0800201 return xt
Joe Tsai08e00302018-11-26 22:32:06 -0800202}
203
Joe Tsai0fc49f82019-05-01 12:29:25 -0700204// ExtensionTypeOf returns a protoreflect.ExtensionType where the type of the
205// field is t. The type t must be provided if the field is an enum or message.
Joe Tsai08e00302018-11-26 22:32:06 -0800206//
Joe Tsai0fc49f82019-05-01 12:29:25 -0700207// This is exported for testing purposes.
208func ExtensionTypeOf(xd pref.ExtensionDescriptor, t reflect.Type) pref.ExtensionType {
209 // Extension types for non-enums and non-messages are simple.
210 switch xd.Kind() {
211 case pref.EnumKind, pref.MessageKind, pref.GroupKind:
212 default:
213 return ptype.GoExtension(xd, nil, nil)
214 }
215
216 // Create an ExtensionType where GoType is the wrapper type.
Joe Tsai08e00302018-11-26 22:32:06 -0800217 conv := pvalue.NewLegacyConverter(t, xd.Kind(), Export{})
218 xt := ptype.GoExtension(xd, conv.EnumType, conv.MessageType)
Joe Tsai0fc49f82019-05-01 12:29:25 -0700219 if !conv.IsLegacy {
220 return xt
221 }
Joe Tsai08e00302018-11-26 22:32:06 -0800222
Joe Tsai0fc49f82019-05-01 12:29:25 -0700223 // Wrap ExtensionType such that GoType presents the legacy Go type.
Joe Tsai6dbffb72018-12-04 14:06:19 -0800224 xt2 := &extensionType{ExtensionType: xt}
Joe Tsai08e00302018-11-26 22:32:06 -0800225 if xd.Cardinality() != pref.Repeated {
226 xt2.typ = t
Joe Tsaid18bd312019-01-09 03:23:55 -0800227 xt2.new = func() pref.Value {
228 return xt.New()
Joe Tsai08e00302018-11-26 22:32:06 -0800229 }
230 xt2.valueOf = func(v interface{}) pref.Value {
231 if reflect.TypeOf(v) != xt2.typ {
232 panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
233 }
234 if xd.Kind() == pref.EnumKind {
235 return xt.ValueOf(Export{}.EnumOf(v))
236 } else {
Joe Tsai22b1ebd2019-03-11 13:45:14 -0700237 return xt.ValueOf(Export{}.MessageOf(v).Interface())
Joe Tsai08e00302018-11-26 22:32:06 -0800238 }
239 }
240 xt2.interfaceOf = func(v pref.Value) interface{} {
241 return xt.InterfaceOf(v).(pvalue.Unwrapper).ProtoUnwrap()
242 }
243 } else {
244 xt2.typ = reflect.PtrTo(reflect.SliceOf(t))
Joe Tsaid18bd312019-01-09 03:23:55 -0800245 xt2.new = func() pref.Value {
246 v := reflect.New(xt2.typ.Elem()).Interface()
247 return pref.ValueOf(pvalue.ListOf(v, conv))
Joe Tsai08e00302018-11-26 22:32:06 -0800248 }
249 xt2.valueOf = func(v interface{}) pref.Value {
250 if reflect.TypeOf(v) != xt2.typ {
251 panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
252 }
253 return pref.ValueOf(pvalue.ListOf(v, conv))
254 }
255 xt2.interfaceOf = func(pv pref.Value) interface{} {
256 v := pv.List().(pvalue.Unwrapper).ProtoUnwrap()
257 if reflect.TypeOf(v) != xt2.typ {
258 panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
259 }
260 return v
261 }
262 }
263 return xt2
264}
265
Joe Tsai6dbffb72018-12-04 14:06:19 -0800266type extensionType struct {
Joe Tsai08e00302018-11-26 22:32:06 -0800267 pref.ExtensionType
268 typ reflect.Type
Joe Tsaid18bd312019-01-09 03:23:55 -0800269 new func() pref.Value
Joe Tsai08e00302018-11-26 22:32:06 -0800270 valueOf func(interface{}) pref.Value
271 interfaceOf func(pref.Value) interface{}
272}
273
Joe Tsai6dbffb72018-12-04 14:06:19 -0800274func (x *extensionType) GoType() reflect.Type { return x.typ }
Joe Tsaid18bd312019-01-09 03:23:55 -0800275func (x *extensionType) New() pref.Value { return x.new() }
Joe Tsai6dbffb72018-12-04 14:06:19 -0800276func (x *extensionType) ValueOf(v interface{}) pref.Value { return x.valueOf(v) }
277func (x *extensionType) InterfaceOf(v pref.Value) interface{} { return x.interfaceOf(v) }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700278func (x *extensionType) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, x.Descriptor()) }