blob: 19205ea0f7c7db03c6f459eaf8d7453b9b227321 [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 Tsai4fddeba2019-03-20 18:29:32 -070018 piface "github.com/golang/protobuf/v2/runtime/protoiface"
Joe Tsai08e00302018-11-26 22:32:06 -080019)
20
Joe Tsai4fddeba2019-03-20 18:29:32 -070021// extensionDescKey is a comparable version of protoiface.ExtensionDescV1
Joe Tsai62517cc2018-12-04 14:00:01 -080022// suitable for use as a key in a map.
23type extensionDescKey struct {
24 typeV2 pref.ExtensionType
25 extendedType reflect.Type
26 extensionType reflect.Type
27 field int32
28 name string
29 tag string
30 filename string
31}
32
Joe Tsai4fddeba2019-03-20 18:29:32 -070033func extensionDescKeyOf(d *piface.ExtensionDescV1) extensionDescKey {
Joe Tsai62517cc2018-12-04 14:00:01 -080034 return extensionDescKey{
35 d.Type,
36 reflect.TypeOf(d.ExtendedType),
37 reflect.TypeOf(d.ExtensionType),
38 d.Field, d.Name, d.Tag, d.Filename,
39 }
40}
41
42var (
43 extensionTypeCache sync.Map // map[extensionDescKey]protoreflect.ExtensionType
Joe Tsai4fddeba2019-03-20 18:29:32 -070044 extensionDescCache sync.Map // map[protoreflect.ExtensionType]*protoiface.ExtensionDescV1
Joe Tsai62517cc2018-12-04 14:00:01 -080045)
46
Joe Tsai6dbffb72018-12-04 14:06:19 -080047// extensionDescFromType converts a v2 protoreflect.ExtensionType to a
Joe Tsai4fddeba2019-03-20 18:29:32 -070048// protoiface.ExtensionDescV1. The returned ExtensionDesc must not be mutated.
49func extensionDescFromType(t pref.ExtensionType) *piface.ExtensionDescV1 {
Joe Tsaiafb455e2019-03-14 16:08:22 -070050 // Fast-path: check whether an extension desc is already nested within.
Joe Tsai4fddeba2019-03-20 18:29:32 -070051 if t, ok := t.(interface {
52 ProtoLegacyExtensionDesc() *piface.ExtensionDescV1
53 }); ok {
Joe Tsaiafb455e2019-03-14 16:08:22 -070054 if d := t.ProtoLegacyExtensionDesc(); d != nil {
55 return d
56 }
57 }
58
Joe Tsai62517cc2018-12-04 14:00:01 -080059 // Fast-path: check the cache for whether this ExtensionType has already
60 // been converted to a legacy descriptor.
61 if d, ok := extensionDescCache.Load(t); ok {
Joe Tsai4fddeba2019-03-20 18:29:32 -070062 return d.(*piface.ExtensionDescV1)
Joe Tsai08e00302018-11-26 22:32:06 -080063 }
64
65 // Determine the parent type if possible.
Joe Tsai4fddeba2019-03-20 18:29:32 -070066 var parent piface.MessageV1
Joe Tsai08e00302018-11-26 22:32:06 -080067 if mt, ok := t.ExtendedType().(pref.MessageType); ok {
68 // Create a new parent message and unwrap it if possible.
Joe Tsai3bc7d6f2019-01-09 02:57:13 -080069 mv := mt.New().Interface()
Joe Tsai08e00302018-11-26 22:32:06 -080070 t := reflect.TypeOf(mv)
71 if mv, ok := mv.(pvalue.Unwrapper); ok {
72 t = reflect.TypeOf(mv.ProtoUnwrap())
73 }
74
75 // Check whether the message implements the legacy v1 Message interface.
76 mz := reflect.Zero(t).Interface()
Joe Tsai4fddeba2019-03-20 18:29:32 -070077 if mz, ok := mz.(piface.MessageV1); ok {
Joe Tsai08e00302018-11-26 22:32:06 -080078 parent = mz
79 }
80 }
81
82 // Determine the v1 extension type, which is unfortunately not the same as
83 // the v2 ExtensionType.GoType.
84 extType := t.GoType()
85 switch extType.Kind() {
86 case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
87 extType = reflect.PtrTo(extType) // T -> *T for singular scalar fields
88 case reflect.Ptr:
89 if extType.Elem().Kind() == reflect.Slice {
90 extType = extType.Elem() // *[]T -> []T for repeated fields
91 }
92 }
93
94 // Reconstruct the legacy enum full name, which is an odd mixture of the
95 // proto package name with the Go type name.
96 var enumName string
97 if t.Kind() == pref.EnumKind {
98 // Derive Go type name.
99 // For legacy enums, unwrap the wrapper to get the underlying Go type.
100 et := t.EnumType().(pref.EnumType)
101 var ev interface{} = et.New(0)
102 if u, ok := ev.(pvalue.Unwrapper); ok {
103 ev = u.ProtoUnwrap()
104 }
105 enumName = reflect.TypeOf(ev).Name()
106
107 // Derive the proto package name.
108 // For legacy enums, obtain the proto package from the raw descriptor.
109 var protoPkg string
110 if fd := parentFileDescriptor(et); fd != nil {
111 protoPkg = string(fd.Package())
112 }
Joe Tsai6dbffb72018-12-04 14:06:19 -0800113 if ed, ok := ev.(enumV1); ok && protoPkg == "" {
Joe Tsai08e00302018-11-26 22:32:06 -0800114 b, _ := ed.EnumDescriptor()
Damien Neil987d5702019-04-10 11:06:53 -0700115 protoPkg = loadFileDesc(b).GetPackage()
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
125 if fd := parentFileDescriptor(t); fd != nil {
126 filename = fd.Path()
127 }
128
Joe Tsai4fddeba2019-03-20 18:29:32 -0700129 // Construct and return a ExtensionDescV1.
130 d := &piface.ExtensionDescV1{
Joe Tsai08e00302018-11-26 22:32:06 -0800131 Type: t,
132 ExtendedType: parent,
133 ExtensionType: reflect.Zero(extType).Interface(),
134 Field: int32(t.Number()),
135 Name: string(t.FullName()),
136 Tag: ptag.Marshal(t, enumName),
137 Filename: filename,
138 }
Joe Tsaib9365042019-03-19 14:14:29 -0700139 if d, ok := extensionDescCache.LoadOrStore(t, 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 Tsai4fddeba2019-03-20 18:29:32 -0700145// extensionTypeFromDesc 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 Tsai4fddeba2019-03-20 18:29:32 -0700149func extensionTypeFromDesc(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.
157 dk := extensionDescKeyOf(d)
158 if t, ok := extensionTypeCache.Load(dk); ok {
159 return t.(pref.ExtensionType)
Joe Tsai08e00302018-11-26 22:32:06 -0800160 }
161
162 // Derive basic field information from the struct tag.
163 t := reflect.TypeOf(d.ExtensionType)
164 isOptional := t.Kind() == reflect.Ptr && t.Elem().Kind() != reflect.Struct
165 isRepeated := t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
166 if isOptional || isRepeated {
167 t = t.Elem()
168 }
169 f := ptag.Unmarshal(d.Tag, t)
170
171 // Construct a v2 ExtensionType.
172 conv := pvalue.NewLegacyConverter(t, f.Kind, Export{})
173 xd, err := ptype.NewExtension(&ptype.StandaloneExtension{
174 FullName: pref.FullName(d.Name),
175 Number: pref.FieldNumber(d.Field),
176 Cardinality: f.Cardinality,
177 Kind: f.Kind,
178 Default: f.Default,
179 Options: f.Options,
180 EnumType: conv.EnumType,
181 MessageType: conv.MessageType,
Herbie Ong2b0bba82018-12-20 12:56:42 -0800182 ExtendedType: pimpl.Export{}.MessageTypeOf(d.ExtendedType),
Joe Tsai08e00302018-11-26 22:32:06 -0800183 })
184 if err != nil {
185 panic(err)
186 }
Herbie Ong2b0bba82018-12-20 12:56:42 -0800187 var zv interface{}
188 switch xd.Kind() {
189 case pref.EnumKind, pref.MessageKind, pref.GroupKind:
190 zv = reflect.Zero(t).Interface()
191 }
192 xt := pimpl.Export{}.ExtensionTypeOf(xd, zv)
Joe Tsai08e00302018-11-26 22:32:06 -0800193
Joe Tsai62517cc2018-12-04 14:00:01 -0800194 // Cache the conversion for both directions.
Joe Tsaib9365042019-03-19 14:14:29 -0700195 extensionDescCache.LoadOrStore(xt, d)
196 if xt, ok := extensionTypeCache.LoadOrStore(dk, xt); ok {
197 return xt.(pref.ExtensionType)
198 }
Joe Tsai62517cc2018-12-04 14:00:01 -0800199 return xt
Joe Tsai08e00302018-11-26 22:32:06 -0800200}
201
Joe Tsai6dbffb72018-12-04 14:06:19 -0800202// extensionTypeOf returns a protoreflect.ExtensionType where the GoType
Joe Tsai08e00302018-11-26 22:32:06 -0800203// is the underlying v1 Go type instead of the wrapper types used to present
204// v1 Go types as if they satisfied the v2 API.
205//
206// This function is only valid if xd.Kind is an enum or message.
Joe Tsai6dbffb72018-12-04 14:06:19 -0800207func extensionTypeOf(xd pref.ExtensionDescriptor, t reflect.Type) pref.ExtensionType {
Joe Tsai08e00302018-11-26 22:32:06 -0800208 // Step 1: Create an ExtensionType where GoType is the wrapper type.
209 conv := pvalue.NewLegacyConverter(t, xd.Kind(), Export{})
210 xt := ptype.GoExtension(xd, conv.EnumType, conv.MessageType)
211
212 // Step 2: Wrap ExtensionType such that GoType presents the legacy Go type.
Joe Tsai6dbffb72018-12-04 14:06:19 -0800213 xt2 := &extensionType{ExtensionType: xt}
Joe Tsai08e00302018-11-26 22:32:06 -0800214 if xd.Cardinality() != pref.Repeated {
215 xt2.typ = t
Joe Tsaid18bd312019-01-09 03:23:55 -0800216 xt2.new = func() pref.Value {
217 return xt.New()
Joe Tsai08e00302018-11-26 22:32:06 -0800218 }
219 xt2.valueOf = func(v interface{}) pref.Value {
220 if reflect.TypeOf(v) != xt2.typ {
221 panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
222 }
223 if xd.Kind() == pref.EnumKind {
224 return xt.ValueOf(Export{}.EnumOf(v))
225 } else {
Joe Tsai22b1ebd2019-03-11 13:45:14 -0700226 return xt.ValueOf(Export{}.MessageOf(v).Interface())
Joe Tsai08e00302018-11-26 22:32:06 -0800227 }
228 }
229 xt2.interfaceOf = func(v pref.Value) interface{} {
230 return xt.InterfaceOf(v).(pvalue.Unwrapper).ProtoUnwrap()
231 }
232 } else {
233 xt2.typ = reflect.PtrTo(reflect.SliceOf(t))
Joe Tsaid18bd312019-01-09 03:23:55 -0800234 xt2.new = func() pref.Value {
235 v := reflect.New(xt2.typ.Elem()).Interface()
236 return pref.ValueOf(pvalue.ListOf(v, conv))
Joe Tsai08e00302018-11-26 22:32:06 -0800237 }
238 xt2.valueOf = func(v interface{}) pref.Value {
239 if reflect.TypeOf(v) != xt2.typ {
240 panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
241 }
242 return pref.ValueOf(pvalue.ListOf(v, conv))
243 }
244 xt2.interfaceOf = func(pv pref.Value) interface{} {
245 v := pv.List().(pvalue.Unwrapper).ProtoUnwrap()
246 if reflect.TypeOf(v) != xt2.typ {
247 panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
248 }
249 return v
250 }
251 }
252 return xt2
253}
254
Joe Tsai6dbffb72018-12-04 14:06:19 -0800255type extensionType struct {
Joe Tsai08e00302018-11-26 22:32:06 -0800256 pref.ExtensionType
257 typ reflect.Type
Joe Tsaid18bd312019-01-09 03:23:55 -0800258 new func() pref.Value
Joe Tsai08e00302018-11-26 22:32:06 -0800259 valueOf func(interface{}) pref.Value
260 interfaceOf func(pref.Value) interface{}
261}
262
Joe Tsai6dbffb72018-12-04 14:06:19 -0800263func (x *extensionType) GoType() reflect.Type { return x.typ }
Joe Tsaid18bd312019-01-09 03:23:55 -0800264func (x *extensionType) New() pref.Value { return x.new() }
Joe Tsai6dbffb72018-12-04 14:06:19 -0800265func (x *extensionType) ValueOf(v interface{}) pref.Value { return x.valueOf(v) }
266func (x *extensionType) InterfaceOf(v pref.Value) interface{} { return x.interfaceOf(v) }
Joe Tsaib3960a72018-12-05 10:35:35 -0800267func (x *extensionType) Format(s fmt.State, r rune) { pfmt.FormatDesc(s, r, x) }