blob: 700bc43d2aab4a8b3aaf42c0d92981409799846a [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"
10
11 papi "github.com/golang/protobuf/protoapi"
12 ptag "github.com/golang/protobuf/v2/internal/encoding/tag"
13 pimpl "github.com/golang/protobuf/v2/internal/impl"
14 pvalue "github.com/golang/protobuf/v2/internal/value"
15 pref "github.com/golang/protobuf/v2/reflect/protoreflect"
16 ptype "github.com/golang/protobuf/v2/reflect/prototype"
17)
18
19func legacyExtensionDescFromType(t pref.ExtensionType) *papi.ExtensionDesc {
20 if t, ok := t.(dualExtensionType); ok {
21 return t.desc
22 }
23
24 // Determine the parent type if possible.
25 var parent papi.Message
26 if mt, ok := t.ExtendedType().(pref.MessageType); ok {
27 // Create a new parent message and unwrap it if possible.
28 mv := mt.New()
29 t := reflect.TypeOf(mv)
30 if mv, ok := mv.(pvalue.Unwrapper); ok {
31 t = reflect.TypeOf(mv.ProtoUnwrap())
32 }
33
34 // Check whether the message implements the legacy v1 Message interface.
35 mz := reflect.Zero(t).Interface()
36 if mz, ok := mz.(papi.Message); ok {
37 parent = mz
38 }
39 }
40
41 // Determine the v1 extension type, which is unfortunately not the same as
42 // the v2 ExtensionType.GoType.
43 extType := t.GoType()
44 switch extType.Kind() {
45 case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
46 extType = reflect.PtrTo(extType) // T -> *T for singular scalar fields
47 case reflect.Ptr:
48 if extType.Elem().Kind() == reflect.Slice {
49 extType = extType.Elem() // *[]T -> []T for repeated fields
50 }
51 }
52
53 // Reconstruct the legacy enum full name, which is an odd mixture of the
54 // proto package name with the Go type name.
55 var enumName string
56 if t.Kind() == pref.EnumKind {
57 // Derive Go type name.
58 // For legacy enums, unwrap the wrapper to get the underlying Go type.
59 et := t.EnumType().(pref.EnumType)
60 var ev interface{} = et.New(0)
61 if u, ok := ev.(pvalue.Unwrapper); ok {
62 ev = u.ProtoUnwrap()
63 }
64 enumName = reflect.TypeOf(ev).Name()
65
66 // Derive the proto package name.
67 // For legacy enums, obtain the proto package from the raw descriptor.
68 var protoPkg string
69 if fd := parentFileDescriptor(et); fd != nil {
70 protoPkg = string(fd.Package())
71 }
72 if ed, ok := ev.(legacyEnum); ok && protoPkg == "" {
73 b, _ := ed.EnumDescriptor()
74 protoPkg = legacyLoadFileDesc(b).GetPackage()
75 }
76
77 if protoPkg != "" {
78 enumName = protoPkg + "." + enumName
79 }
80 }
81
82 // Derive the proto file that the extension was declared within.
83 var filename string
84 if fd := parentFileDescriptor(t); fd != nil {
85 filename = fd.Path()
86 }
87
88 // Construct and return a v1 ExtensionDesc.
89 return &papi.ExtensionDesc{
90 Type: t,
91 ExtendedType: parent,
92 ExtensionType: reflect.Zero(extType).Interface(),
93 Field: int32(t.Number()),
94 Name: string(t.FullName()),
95 Tag: ptag.Marshal(t, enumName),
96 Filename: filename,
97 }
98}
99
100func legacyExtensionTypeFromDesc(d *papi.ExtensionDesc) pref.ExtensionType {
101 if d.Type != nil {
102 return dualExtensionType{d.Type, d}
103 }
104
105 // Derive basic field information from the struct tag.
106 t := reflect.TypeOf(d.ExtensionType)
107 isOptional := t.Kind() == reflect.Ptr && t.Elem().Kind() != reflect.Struct
108 isRepeated := t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
109 if isOptional || isRepeated {
110 t = t.Elem()
111 }
112 f := ptag.Unmarshal(d.Tag, t)
113
114 // Construct a v2 ExtensionType.
115 conv := pvalue.NewLegacyConverter(t, f.Kind, Export{})
116 xd, err := ptype.NewExtension(&ptype.StandaloneExtension{
117 FullName: pref.FullName(d.Name),
118 Number: pref.FieldNumber(d.Field),
119 Cardinality: f.Cardinality,
120 Kind: f.Kind,
121 Default: f.Default,
122 Options: f.Options,
123 EnumType: conv.EnumType,
124 MessageType: conv.MessageType,
125 ExtendedType: Export{}.MessageTypeOf(d.ExtendedType),
126 })
127 if err != nil {
128 panic(err)
129 }
130 xt := pimpl.Export{}.ExtensionTypeOf(xd, reflect.Zero(t).Interface())
131 return dualExtensionType{xt, d}
132}
133
134type dualExtensionType struct {
135 pref.ExtensionType
136 desc *papi.ExtensionDesc
137}
138
139// TODO: Provide custom stringer for dualExtensionType.
140
141// legacyExtensionTypeOf returns a protoreflect.ExtensionType where the GoType
142// is the underlying v1 Go type instead of the wrapper types used to present
143// v1 Go types as if they satisfied the v2 API.
144//
145// This function is only valid if xd.Kind is an enum or message.
146func legacyExtensionTypeOf(xd pref.ExtensionDescriptor, t reflect.Type) pref.ExtensionType {
147 // Step 1: Create an ExtensionType where GoType is the wrapper type.
148 conv := pvalue.NewLegacyConverter(t, xd.Kind(), Export{})
149 xt := ptype.GoExtension(xd, conv.EnumType, conv.MessageType)
150
151 // Step 2: Wrap ExtensionType such that GoType presents the legacy Go type.
152 xt2 := &legacyExtensionType{ExtensionType: xt}
153 if xd.Cardinality() != pref.Repeated {
154 xt2.typ = t
155 xt2.new = func() interface{} {
156 return xt.New().(pvalue.Unwrapper).ProtoUnwrap()
157 }
158 xt2.valueOf = func(v interface{}) pref.Value {
159 if reflect.TypeOf(v) != xt2.typ {
160 panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
161 }
162 if xd.Kind() == pref.EnumKind {
163 return xt.ValueOf(Export{}.EnumOf(v))
164 } else {
165 return xt.ValueOf(Export{}.MessageOf(v))
166 }
167 }
168 xt2.interfaceOf = func(v pref.Value) interface{} {
169 return xt.InterfaceOf(v).(pvalue.Unwrapper).ProtoUnwrap()
170 }
171 } else {
172 xt2.typ = reflect.PtrTo(reflect.SliceOf(t))
173 xt2.new = func() interface{} {
174 return reflect.New(xt2.typ.Elem()).Interface()
175 }
176 xt2.valueOf = func(v interface{}) pref.Value {
177 if reflect.TypeOf(v) != xt2.typ {
178 panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
179 }
180 return pref.ValueOf(pvalue.ListOf(v, conv))
181 }
182 xt2.interfaceOf = func(pv pref.Value) interface{} {
183 v := pv.List().(pvalue.Unwrapper).ProtoUnwrap()
184 if reflect.TypeOf(v) != xt2.typ {
185 panic(fmt.Sprintf("invalid type: got %T, want %v", v, xt2.typ))
186 }
187 return v
188 }
189 }
190 return xt2
191}
192
193type legacyExtensionType struct {
194 pref.ExtensionType
195 typ reflect.Type
196 new func() interface{}
197 valueOf func(interface{}) pref.Value
198 interfaceOf func(pref.Value) interface{}
199}
200
201func (x *legacyExtensionType) GoType() reflect.Type { return x.typ }
202func (x *legacyExtensionType) New() interface{} { return x.new() }
203func (x *legacyExtensionType) ValueOf(v interface{}) pref.Value { return x.valueOf(v) }
204func (x *legacyExtensionType) InterfaceOf(v pref.Value) interface{} { return x.interfaceOf(v) }
205
206// TODO: Provide custom stringer with the new GoType.