blob: 462248432b1b65fcfdfd4f7861184ebf10869268 [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
Damien Neilf1e905b2019-08-08 15:45:59 -070018var legacyExtensionInfoCache sync.Map // map[protoreflect.ExtensionType]*ExtensionInfo
Joe Tsai62517cc2018-12-04 14:00:01 -080019
Damien Neilf1e905b2019-08-08 15:45:59 -070020// legacyExtensionDescFromType converts a protoreflect.ExtensionType to an
21// ExtensionInfo. The returned ExtensionInfo must not be mutated.
22func legacyExtensionDescFromType(xt pref.ExtensionType) *ExtensionInfo {
23 // Fast-path: check whether this is an ExtensionInfo.
24 if xt, ok := xt.(*ExtensionInfo); ok {
25 return xt
Joe Tsaiafb455e2019-03-14 16:08:22 -070026 }
27
Joe Tsai62517cc2018-12-04 14:00:01 -080028 // Fast-path: check the cache for whether this ExtensionType has already
Damien Neilf1e905b2019-08-08 15:45:59 -070029 // been converted to an ExtensionInfo.
30 if d, ok := legacyExtensionInfoCache.Load(xt); ok {
31 return d.(*ExtensionInfo)
Joe Tsai08e00302018-11-26 22:32:06 -080032 }
33
Damien Neilf1e905b2019-08-08 15:45:59 -070034 tt := xt.GoType()
35 if xt.Descriptor().Cardinality() == pref.Repeated {
36 tt = tt.Elem().Elem()
37 }
38 xi := &ExtensionInfo{}
39 InitExtensionInfo(xi, xt.Descriptor().Descriptor(), tt)
40 xi.lazyInit() // populate legacy fields
41
42 if xi, ok := legacyExtensionInfoCache.LoadOrStore(xt, xi); ok {
43 return xi.(*ExtensionInfo)
44 }
45 return xi
46}
47
48func (xi *ExtensionInfo) initToLegacy() {
49 xd := xi.desc
Joe Tsai4fddeba2019-03-20 18:29:32 -070050 var parent piface.MessageV1
Damien Neil92f76182019-08-02 16:58:08 -070051 messageName := xd.ContainingMessage().FullName()
Joe Tsaiac31a352019-05-13 14:32:56 -070052 if mt, _ := preg.GlobalTypes.FindMessageByName(messageName); mt != nil {
Joe Tsai08e00302018-11-26 22:32:06 -080053 // Create a new parent message and unwrap it if possible.
Joe Tsai3bc7d6f2019-01-09 02:57:13 -080054 mv := mt.New().Interface()
Joe Tsai08e00302018-11-26 22:32:06 -080055 t := reflect.TypeOf(mv)
Damien Neil954bd922019-07-17 16:52:10 -070056 if mv, ok := mv.(Unwrapper); ok {
Joe Tsai08e00302018-11-26 22:32:06 -080057 t = reflect.TypeOf(mv.ProtoUnwrap())
58 }
59
60 // Check whether the message implements the legacy v1 Message interface.
61 mz := reflect.Zero(t).Interface()
Joe Tsai4fddeba2019-03-20 18:29:32 -070062 if mz, ok := mz.(piface.MessageV1); ok {
Joe Tsai08e00302018-11-26 22:32:06 -080063 parent = mz
64 }
65 }
66
67 // Determine the v1 extension type, which is unfortunately not the same as
68 // the v2 ExtensionType.GoType.
Damien Neilf1e905b2019-08-08 15:45:59 -070069 extType := xi.goType
Joe Tsai08e00302018-11-26 22:32:06 -080070 switch extType.Kind() {
71 case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
72 extType = reflect.PtrTo(extType) // T -> *T for singular scalar fields
73 case reflect.Ptr:
74 if extType.Elem().Kind() == reflect.Slice {
75 extType = extType.Elem() // *[]T -> []T for repeated fields
76 }
77 }
78
79 // Reconstruct the legacy enum full name, which is an odd mixture of the
80 // proto package name with the Go type name.
81 var enumName string
Damien Neil92f76182019-08-02 16:58:08 -070082 if xd.Kind() == pref.EnumKind {
Joe Tsai08e00302018-11-26 22:32:06 -080083 // Derive Go type name.
Joe Tsai0fc49f82019-05-01 12:29:25 -070084 t := extType
85 if t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice {
86 t = t.Elem()
Joe Tsai08e00302018-11-26 22:32:06 -080087 }
Joe Tsai0fc49f82019-05-01 12:29:25 -070088 enumName = t.Name()
Joe Tsai08e00302018-11-26 22:32:06 -080089
90 // Derive the proto package name.
91 // For legacy enums, obtain the proto package from the raw descriptor.
92 var protoPkg string
Damien Neil92f76182019-08-02 16:58:08 -070093 if fd := xd.Enum().ParentFile(); fd != nil {
Joe Tsai08e00302018-11-26 22:32:06 -080094 protoPkg = string(fd.Package())
95 }
Joe Tsai0fc49f82019-05-01 12:29:25 -070096 if ed, ok := reflect.Zero(t).Interface().(enumV1); ok && protoPkg == "" {
Joe Tsai08e00302018-11-26 22:32:06 -080097 b, _ := ed.EnumDescriptor()
Joe Tsaid8881392019-06-06 13:01:53 -070098 protoPkg = string(legacyLoadFileDesc(b).Package())
Joe Tsai08e00302018-11-26 22:32:06 -080099 }
100
101 if protoPkg != "" {
102 enumName = protoPkg + "." + enumName
103 }
104 }
105
106 // Derive the proto file that the extension was declared within.
107 var filename string
Damien Neil92f76182019-08-02 16:58:08 -0700108 if fd := xd.ParentFile(); fd != nil {
Joe Tsai08e00302018-11-26 22:32:06 -0800109 filename = fd.Path()
110 }
111
Damien Neilf1e905b2019-08-08 15:45:59 -0700112 xi.ExtendedType = parent
113 xi.ExtensionType = reflect.Zero(extType).Interface()
114 xi.Field = int32(xd.Number())
115 xi.Name = string(xd.FullName())
116 xi.Tag = ptag.Marshal(xd, enumName)
117 xi.Filename = filename
Joe Tsai08e00302018-11-26 22:32:06 -0800118}
119
Damien Neilf1e905b2019-08-08 15:45:59 -0700120// initFromLegacy initializes an ExtensionInfo from
121// the contents of the deprecated exported fields of the type.
122func (xi *ExtensionInfo) initFromLegacy() {
Joe Tsaid8881392019-06-06 13:01:53 -0700123 // Resolve enum or message dependencies.
124 var ed pref.EnumDescriptor
125 var md pref.MessageDescriptor
Damien Neilf1e905b2019-08-08 15:45:59 -0700126 t := reflect.TypeOf(xi.ExtensionType)
Joe Tsai08e00302018-11-26 22:32:06 -0800127 isOptional := t.Kind() == reflect.Ptr && t.Elem().Kind() != reflect.Struct
128 isRepeated := t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
129 if isOptional || isRepeated {
130 t = t.Elem()
131 }
Joe Tsaid8881392019-06-06 13:01:53 -0700132 switch v := reflect.Zero(t).Interface().(type) {
133 case pref.Enum:
134 ed = v.Descriptor()
135 case enumV1:
136 ed = LegacyLoadEnumDesc(t)
137 case pref.ProtoMessage:
138 md = v.ProtoReflect().Descriptor()
139 case messageV1:
140 md = LegacyLoadMessageDesc(t)
141 }
142
143 // Derive basic field information from the struct tag.
144 var evs pref.EnumValueDescriptors
145 if ed != nil {
146 evs = ed.Values()
147 }
Damien Neilf1e905b2019-08-08 15:45:59 -0700148 fd := ptag.Unmarshal(xi.Tag, t, evs).(*filedesc.Field)
Joe Tsai08e00302018-11-26 22:32:06 -0800149
150 // Construct a v2 ExtensionType.
Joe Tsaid8881392019-06-06 13:01:53 -0700151 xd := &filedesc.Extension{L2: new(filedesc.ExtensionL2)}
152 xd.L0.ParentFile = filedesc.SurrogateProto2
Damien Neilf1e905b2019-08-08 15:45:59 -0700153 xd.L0.FullName = pref.FullName(xi.Name)
154 xd.L1.Number = pref.FieldNumber(xi.Field)
Joe Tsaid8881392019-06-06 13:01:53 -0700155 xd.L2.Cardinality = fd.L1.Cardinality
156 xd.L1.Kind = fd.L1.Kind
157 xd.L2.IsPacked = fd.L1.IsPacked
158 xd.L2.Default = fd.L1.Default
Damien Neilf1e905b2019-08-08 15:45:59 -0700159 xd.L1.Extendee = Export{}.MessageDescriptorOf(xi.ExtendedType)
Joe Tsaid8881392019-06-06 13:01:53 -0700160 xd.L2.Enum = ed
161 xd.L2.Message = md
Damien Neilf1e905b2019-08-08 15:45:59 -0700162 tt := reflect.TypeOf(xi.ExtensionType)
Damien Neil954bd922019-07-17 16:52:10 -0700163 if isOptional {
164 tt = tt.Elem()
165 } else if isRepeated {
166 tt = reflect.PtrTo(tt)
167 }
Joe Tsai08e00302018-11-26 22:32:06 -0800168
Damien Neilf1e905b2019-08-08 15:45:59 -0700169 xi.desc = xd
170 xi.goType = tt
Joe Tsai08e00302018-11-26 22:32:06 -0800171}