blob: 2da4d71b51040c5330a3ecc6b6ac62b62d66d496 [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
Damien Neile89e6242019-05-13 23:55:40 -070011 ptag "google.golang.org/protobuf/internal/encoding/tag"
Joe Tsaid8881392019-06-06 13:01:53 -070012 "google.golang.org/protobuf/internal/filedesc"
Damien Neile89e6242019-05-13 23:55:40 -070013 pref "google.golang.org/protobuf/reflect/protoreflect"
14 preg "google.golang.org/protobuf/reflect/protoregistry"
15 piface "google.golang.org/protobuf/runtime/protoiface"
Joe Tsai08e00302018-11-26 22:32:06 -080016)
17
Joe Tsai21ade492019-05-22 13:42:54 -040018// legacyExtensionDescKey is a comparable version of protoiface.ExtensionDescV1
Joe Tsai62517cc2018-12-04 14:00:01 -080019// suitable for use as a key in a map.
Joe Tsai21ade492019-05-22 13:42:54 -040020type legacyExtensionDescKey struct {
Joe Tsai62517cc2018-12-04 14:00:01 -080021 typeV2 pref.ExtensionType
22 extendedType reflect.Type
23 extensionType reflect.Type
24 field int32
25 name string
26 tag string
27 filename string
28}
29
Joe Tsai21ade492019-05-22 13:42:54 -040030func legacyExtensionDescKeyOf(d *piface.ExtensionDescV1) legacyExtensionDescKey {
31 return legacyExtensionDescKey{
Joe Tsai62517cc2018-12-04 14:00:01 -080032 d.Type,
33 reflect.TypeOf(d.ExtendedType),
34 reflect.TypeOf(d.ExtensionType),
35 d.Field, d.Name, d.Tag, d.Filename,
36 }
37}
38
39var (
Joe Tsai21ade492019-05-22 13:42:54 -040040 legacyExtensionTypeCache sync.Map // map[legacyExtensionDescKey]protoreflect.ExtensionType
41 legacyExtensionDescCache sync.Map // map[protoreflect.ExtensionType]*protoiface.ExtensionDescV1
Joe Tsai62517cc2018-12-04 14:00:01 -080042)
43
Joe Tsai21ade492019-05-22 13:42:54 -040044// legacyExtensionDescFromType converts a v2 protoreflect.ExtensionType to a
Joe Tsai4fddeba2019-03-20 18:29:32 -070045// protoiface.ExtensionDescV1. The returned ExtensionDesc must not be mutated.
Joe Tsai21ade492019-05-22 13:42:54 -040046func legacyExtensionDescFromType(xt pref.ExtensionType) *piface.ExtensionDescV1 {
Joe Tsaiafb455e2019-03-14 16:08:22 -070047 // Fast-path: check whether an extension desc is already nested within.
Joe Tsai0fc49f82019-05-01 12:29:25 -070048 if xt, ok := xt.(interface {
Joe Tsai4fddeba2019-03-20 18:29:32 -070049 ProtoLegacyExtensionDesc() *piface.ExtensionDescV1
50 }); ok {
Joe Tsai0fc49f82019-05-01 12:29:25 -070051 if d := xt.ProtoLegacyExtensionDesc(); d != nil {
Joe Tsaiafb455e2019-03-14 16:08:22 -070052 return d
53 }
54 }
55
Joe Tsai62517cc2018-12-04 14:00:01 -080056 // Fast-path: check the cache for whether this ExtensionType has already
57 // been converted to a legacy descriptor.
Joe Tsai21ade492019-05-22 13:42:54 -040058 if d, ok := legacyExtensionDescCache.Load(xt); ok {
Joe Tsai4fddeba2019-03-20 18:29:32 -070059 return d.(*piface.ExtensionDescV1)
Joe Tsai08e00302018-11-26 22:32:06 -080060 }
61
62 // Determine the parent type if possible.
Damien Neil92f76182019-08-02 16:58:08 -070063 xd := xt.Descriptor()
Joe Tsai4fddeba2019-03-20 18:29:32 -070064 var parent piface.MessageV1
Damien Neil92f76182019-08-02 16:58:08 -070065 messageName := xd.ContainingMessage().FullName()
Joe Tsaiac31a352019-05-13 14:32:56 -070066 if mt, _ := preg.GlobalTypes.FindMessageByName(messageName); mt != nil {
Joe Tsai08e00302018-11-26 22:32:06 -080067 // Create a new parent message and unwrap it if possible.
Joe Tsai3bc7d6f2019-01-09 02:57:13 -080068 mv := mt.New().Interface()
Joe Tsai08e00302018-11-26 22:32:06 -080069 t := reflect.TypeOf(mv)
Damien Neil954bd922019-07-17 16:52:10 -070070 if mv, ok := mv.(Unwrapper); ok {
Joe Tsai08e00302018-11-26 22:32:06 -080071 t = reflect.TypeOf(mv.ProtoUnwrap())
72 }
73
74 // Check whether the message implements the legacy v1 Message interface.
75 mz := reflect.Zero(t).Interface()
Joe Tsai4fddeba2019-03-20 18:29:32 -070076 if mz, ok := mz.(piface.MessageV1); ok {
Joe Tsai08e00302018-11-26 22:32:06 -080077 parent = mz
78 }
79 }
80
81 // Determine the v1 extension type, which is unfortunately not the same as
82 // the v2 ExtensionType.GoType.
Joe Tsai0fc49f82019-05-01 12:29:25 -070083 extType := xt.GoType()
Joe Tsai08e00302018-11-26 22:32:06 -080084 switch extType.Kind() {
85 case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
86 extType = reflect.PtrTo(extType) // T -> *T for singular scalar fields
87 case reflect.Ptr:
88 if extType.Elem().Kind() == reflect.Slice {
89 extType = extType.Elem() // *[]T -> []T for repeated fields
90 }
91 }
92
93 // Reconstruct the legacy enum full name, which is an odd mixture of the
94 // proto package name with the Go type name.
95 var enumName string
Damien Neil92f76182019-08-02 16:58:08 -070096 if xd.Kind() == pref.EnumKind {
Joe Tsai08e00302018-11-26 22:32:06 -080097 // Derive Go type name.
Joe Tsai0fc49f82019-05-01 12:29:25 -070098 t := extType
99 if t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice {
100 t = t.Elem()
Joe Tsai08e00302018-11-26 22:32:06 -0800101 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700102 enumName = t.Name()
Joe Tsai08e00302018-11-26 22:32:06 -0800103
104 // Derive the proto package name.
105 // For legacy enums, obtain the proto package from the raw descriptor.
106 var protoPkg string
Damien Neil92f76182019-08-02 16:58:08 -0700107 if fd := xd.Enum().ParentFile(); fd != nil {
Joe Tsai08e00302018-11-26 22:32:06 -0800108 protoPkg = string(fd.Package())
109 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700110 if ed, ok := reflect.Zero(t).Interface().(enumV1); ok && protoPkg == "" {
Joe Tsai08e00302018-11-26 22:32:06 -0800111 b, _ := ed.EnumDescriptor()
Joe Tsaid8881392019-06-06 13:01:53 -0700112 protoPkg = string(legacyLoadFileDesc(b).Package())
Joe Tsai08e00302018-11-26 22:32:06 -0800113 }
114
115 if protoPkg != "" {
116 enumName = protoPkg + "." + enumName
117 }
118 }
119
120 // Derive the proto file that the extension was declared within.
121 var filename string
Damien Neil92f76182019-08-02 16:58:08 -0700122 if fd := xd.ParentFile(); fd != nil {
Joe Tsai08e00302018-11-26 22:32:06 -0800123 filename = fd.Path()
124 }
125
Joe Tsai4fddeba2019-03-20 18:29:32 -0700126 // Construct and return a ExtensionDescV1.
127 d := &piface.ExtensionDescV1{
Joe Tsai0fc49f82019-05-01 12:29:25 -0700128 Type: xt,
Joe Tsai08e00302018-11-26 22:32:06 -0800129 ExtendedType: parent,
130 ExtensionType: reflect.Zero(extType).Interface(),
Damien Neil92f76182019-08-02 16:58:08 -0700131 Field: int32(xd.Number()),
132 Name: string(xd.FullName()),
133 Tag: ptag.Marshal(xd, enumName),
Joe Tsai08e00302018-11-26 22:32:06 -0800134 Filename: filename,
135 }
Joe Tsai21ade492019-05-22 13:42:54 -0400136 if d, ok := legacyExtensionDescCache.LoadOrStore(xt, d); ok {
Joe Tsai4fddeba2019-03-20 18:29:32 -0700137 return d.(*piface.ExtensionDescV1)
Joe Tsaib9365042019-03-19 14:14:29 -0700138 }
Joe Tsai62517cc2018-12-04 14:00:01 -0800139 return d
Joe Tsai08e00302018-11-26 22:32:06 -0800140}
141
Joe Tsai21ade492019-05-22 13:42:54 -0400142// legacyExtensionTypeFromDesc converts a protoiface.ExtensionDescV1 to a
Joe Tsai62517cc2018-12-04 14:00:01 -0800143// v2 protoreflect.ExtensionType. The returned descriptor type takes ownership
144// of the input extension desc. The input must not be mutated so long as the
145// returned type is still in use.
Joe Tsai21ade492019-05-22 13:42:54 -0400146func legacyExtensionTypeFromDesc(d *piface.ExtensionDescV1) pref.ExtensionType {
Joe Tsai62517cc2018-12-04 14:00:01 -0800147 // Fast-path: check whether an extension type is already nested within.
Joe Tsai08e00302018-11-26 22:32:06 -0800148 if d.Type != nil {
Joe Tsai62517cc2018-12-04 14:00:01 -0800149 return d.Type
150 }
151
152 // Fast-path: check the cache for whether this ExtensionType has already
153 // been converted from a legacy descriptor.
Joe Tsai21ade492019-05-22 13:42:54 -0400154 dk := legacyExtensionDescKeyOf(d)
155 if t, ok := legacyExtensionTypeCache.Load(dk); ok {
Joe Tsai62517cc2018-12-04 14:00:01 -0800156 return t.(pref.ExtensionType)
Joe Tsai08e00302018-11-26 22:32:06 -0800157 }
158
Joe Tsaid8881392019-06-06 13:01:53 -0700159 // Resolve enum or message dependencies.
160 var ed pref.EnumDescriptor
161 var md pref.MessageDescriptor
Joe Tsai08e00302018-11-26 22:32:06 -0800162 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 }
Joe Tsaid8881392019-06-06 13:01:53 -0700168 switch v := reflect.Zero(t).Interface().(type) {
169 case pref.Enum:
170 ed = v.Descriptor()
171 case enumV1:
172 ed = LegacyLoadEnumDesc(t)
173 case pref.ProtoMessage:
174 md = v.ProtoReflect().Descriptor()
175 case messageV1:
176 md = LegacyLoadMessageDesc(t)
177 }
178
179 // Derive basic field information from the struct tag.
180 var evs pref.EnumValueDescriptors
181 if ed != nil {
182 evs = ed.Values()
183 }
184 fd := ptag.Unmarshal(d.Tag, t, evs).(*filedesc.Field)
Joe Tsai08e00302018-11-26 22:32:06 -0800185
186 // Construct a v2 ExtensionType.
Joe Tsaid8881392019-06-06 13:01:53 -0700187 xd := &filedesc.Extension{L2: new(filedesc.ExtensionL2)}
188 xd.L0.ParentFile = filedesc.SurrogateProto2
189 xd.L0.FullName = pref.FullName(d.Name)
190 xd.L1.Number = pref.FieldNumber(d.Field)
191 xd.L2.Cardinality = fd.L1.Cardinality
192 xd.L1.Kind = fd.L1.Kind
193 xd.L2.IsPacked = fd.L1.IsPacked
194 xd.L2.Default = fd.L1.Default
195 xd.L1.Extendee = Export{}.MessageDescriptorOf(d.ExtendedType)
196 xd.L2.Enum = ed
197 xd.L2.Message = md
Damien Neil954bd922019-07-17 16:52:10 -0700198 tt := reflect.TypeOf(d.ExtensionType)
199 if isOptional {
200 tt = tt.Elem()
201 } else if isRepeated {
202 tt = reflect.PtrTo(tt)
203 }
204 xt := LegacyExtensionTypeOf(xd, tt)
Joe Tsai08e00302018-11-26 22:32:06 -0800205
Joe Tsai62517cc2018-12-04 14:00:01 -0800206 // Cache the conversion for both directions.
Joe Tsai21ade492019-05-22 13:42:54 -0400207 legacyExtensionDescCache.LoadOrStore(xt, d)
208 if xt, ok := legacyExtensionTypeCache.LoadOrStore(dk, xt); ok {
Joe Tsaib9365042019-03-19 14:14:29 -0700209 return xt.(pref.ExtensionType)
210 }
Joe Tsai62517cc2018-12-04 14:00:01 -0800211 return xt
Joe Tsai08e00302018-11-26 22:32:06 -0800212}
213
Joe Tsai21ade492019-05-22 13:42:54 -0400214// LegacyExtensionTypeOf returns a protoreflect.ExtensionType where the
Damien Neil954bd922019-07-17 16:52:10 -0700215// element type of the field is t.
Joe Tsai08e00302018-11-26 22:32:06 -0800216//
Joe Tsai0fc49f82019-05-01 12:29:25 -0700217// This is exported for testing purposes.
Joe Tsai21ade492019-05-22 13:42:54 -0400218func LegacyExtensionTypeOf(xd pref.ExtensionDescriptor, t reflect.Type) pref.ExtensionType {
Damien Neil92f76182019-08-02 16:58:08 -0700219 xt := &legacyExtensionType{
220 typ: t,
221 conv: NewConverter(t, xd),
Joe Tsai0fc49f82019-05-01 12:29:25 -0700222 }
Damien Neil92f76182019-08-02 16:58:08 -0700223 xt.desc = &extDesc{xd, xt}
224 return xt
Joe Tsai08e00302018-11-26 22:32:06 -0800225}
226
Joe Tsai21ade492019-05-22 13:42:54 -0400227type legacyExtensionType struct {
Damien Neil92f76182019-08-02 16:58:08 -0700228 desc pref.ExtensionTypeDescriptor
Damien Neil954bd922019-07-17 16:52:10 -0700229 typ reflect.Type
230 conv Converter
Joe Tsai08e00302018-11-26 22:32:06 -0800231}
232
Damien Neil954bd922019-07-17 16:52:10 -0700233func (x *legacyExtensionType) GoType() reflect.Type { return x.typ }
234func (x *legacyExtensionType) New() pref.Value { return x.conv.New() }
Damien Neild4f08002019-08-07 12:21:41 -0700235func (x *legacyExtensionType) Zero() pref.Value { return x.conv.Zero() }
Damien Neil954bd922019-07-17 16:52:10 -0700236func (x *legacyExtensionType) ValueOf(v interface{}) pref.Value {
237 return x.conv.PBValueOf(reflect.ValueOf(v))
238}
239func (x *legacyExtensionType) InterfaceOf(v pref.Value) interface{} {
240 return x.conv.GoValueOf(v).Interface()
241}
Damien Neil92f76182019-08-02 16:58:08 -0700242func (x *legacyExtensionType) Descriptor() pref.ExtensionTypeDescriptor { return x.desc }
243
244type extDesc struct {
245 pref.ExtensionDescriptor
246 xt *legacyExtensionType
247}
248
249func (t *extDesc) Type() pref.ExtensionType { return t.xt }
250func (t *extDesc) Descriptor() pref.ExtensionDescriptor { return t.ExtensionDescriptor }