blob: 295798ff58378e301dc9a882c7f42c8fc60638e4 [file] [log] [blame]
Joe Tsai95b02902018-10-31 18:23:42 -07001// 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 impl
6
7import (
8 "reflect"
9 "sync"
10 "unsafe"
11
12 protoV1 "github.com/golang/protobuf/proto"
13 pref "github.com/golang/protobuf/v2/reflect/protoreflect"
14)
15
Joe Tsaif0c01e42018-11-06 13:05:20 -080016// TODO: The logic in the file is a hack and should be in the v1 repository.
17// We need to break the dependency on proto v1 since it is v1 that will
18// eventually need to depend on v2.
19
20// TODO: The v1 API currently exposes no exported functionality for interacting
21// with the extension data structures. We will need to make changes in v1 so
22// that v2 can access these data structures without relying on unsafe.
Joe Tsai95b02902018-10-31 18:23:42 -070023
24var (
25 extTypeA = reflect.TypeOf(map[int32]protoV1.Extension(nil))
26 extTypeB = reflect.TypeOf(protoV1.XXX_InternalExtensions{})
27)
28
29type legacyExtensionIface interface {
30 Len() int
Joe Tsaif0c01e42018-11-06 13:05:20 -080031 Has(pref.FieldNumber) bool
Joe Tsai95b02902018-10-31 18:23:42 -070032 Get(pref.FieldNumber) legacyExtensionEntry
33 Set(pref.FieldNumber, legacyExtensionEntry)
Joe Tsaif0c01e42018-11-06 13:05:20 -080034 Clear(pref.FieldNumber)
Joe Tsai95b02902018-10-31 18:23:42 -070035 Range(f func(pref.FieldNumber, legacyExtensionEntry) bool)
36}
37
38func makeLegacyExtensionMapFunc(t reflect.Type) func(*messageDataType) legacyExtensionIface {
39 fx1, _ := t.FieldByName("XXX_extensions")
40 fx2, _ := t.FieldByName("XXX_InternalExtensions")
41 switch {
42 case fx1.Type == extTypeA:
43 return func(p *messageDataType) legacyExtensionIface {
44 rv := p.p.asType(t).Elem()
45 return (*legacyExtensionMap)(unsafe.Pointer(rv.UnsafeAddr() + fx1.Offset))
46 }
47 case fx2.Type == extTypeB:
48 return func(p *messageDataType) legacyExtensionIface {
49 rv := p.p.asType(t).Elem()
50 return (*legacyExtensionSyncMap)(unsafe.Pointer(rv.UnsafeAddr() + fx2.Offset))
51 }
52 default:
53 return nil
54 }
55}
56
Joe Tsaif0c01e42018-11-06 13:05:20 -080057// TODO: We currently don't do locking with legacyExtensionSyncMap.p.mu.
58// The locking behavior was already obscure "feature" beforehand,
59// and it is not obvious how it translates to the v2 API.
60// The v2 API presents a Range method, which calls a user provided function,
61// which may in turn call other methods on the map. In such a use case,
62// acquiring a lock within each method would result in a reentrant deadlock.
63
Joe Tsai95b02902018-10-31 18:23:42 -070064// legacyExtensionSyncMap is identical to protoV1.XXX_InternalExtensions.
65// It implements legacyExtensionIface.
66type legacyExtensionSyncMap struct {
67 p *struct {
68 mu sync.Mutex
69 m legacyExtensionMap
70 }
71}
72
73func (m legacyExtensionSyncMap) Len() int {
74 if m.p == nil {
75 return 0
76 }
Joe Tsai95b02902018-10-31 18:23:42 -070077 return m.p.m.Len()
78}
Joe Tsaif0c01e42018-11-06 13:05:20 -080079func (m legacyExtensionSyncMap) Has(n pref.FieldNumber) bool {
80 return m.p.m.Has(n)
81}
Joe Tsai95b02902018-10-31 18:23:42 -070082func (m legacyExtensionSyncMap) Get(n pref.FieldNumber) legacyExtensionEntry {
83 if m.p == nil {
84 return legacyExtensionEntry{}
85 }
Joe Tsai95b02902018-10-31 18:23:42 -070086 return m.p.m.Get(n)
87}
88func (m *legacyExtensionSyncMap) Set(n pref.FieldNumber, x legacyExtensionEntry) {
89 if m.p == nil {
90 m.p = new(struct {
91 mu sync.Mutex
92 m legacyExtensionMap
93 })
94 }
Joe Tsai95b02902018-10-31 18:23:42 -070095 m.p.m.Set(n, x)
96}
Joe Tsaif0c01e42018-11-06 13:05:20 -080097func (m legacyExtensionSyncMap) Clear(n pref.FieldNumber) {
98 m.p.m.Clear(n)
99}
Joe Tsai95b02902018-10-31 18:23:42 -0700100func (m legacyExtensionSyncMap) Range(f func(pref.FieldNumber, legacyExtensionEntry) bool) {
101 if m.p == nil {
102 return
103 }
Joe Tsai95b02902018-10-31 18:23:42 -0700104 m.p.m.Range(f)
105}
106
107// legacyExtensionMap is identical to map[int32]protoV1.Extension.
108// It implements legacyExtensionIface.
109type legacyExtensionMap map[pref.FieldNumber]legacyExtensionEntry
110
111func (m legacyExtensionMap) Len() int {
112 return len(m)
113}
Joe Tsaif0c01e42018-11-06 13:05:20 -0800114func (m legacyExtensionMap) Has(n pref.FieldNumber) bool {
115 _, ok := m[n]
116 return ok
117}
Joe Tsai95b02902018-10-31 18:23:42 -0700118func (m legacyExtensionMap) Get(n pref.FieldNumber) legacyExtensionEntry {
119 return m[n]
120}
121func (m *legacyExtensionMap) Set(n pref.FieldNumber, x legacyExtensionEntry) {
122 if *m == nil {
123 *m = make(map[pref.FieldNumber]legacyExtensionEntry)
124 }
125 (*m)[n] = x
126}
Joe Tsaif0c01e42018-11-06 13:05:20 -0800127func (m *legacyExtensionMap) Clear(n pref.FieldNumber) {
128 delete(*m, n)
129}
Joe Tsai95b02902018-10-31 18:23:42 -0700130func (m legacyExtensionMap) Range(f func(pref.FieldNumber, legacyExtensionEntry) bool) {
131 for n, x := range m {
132 if !f(n, x) {
133 return
134 }
135 }
136}
137
138// legacyExtensionEntry is identical to protoV1.Extension.
139type legacyExtensionEntry struct {
140 desc *protoV1.ExtensionDesc
141 val interface{}
142 raw []byte
143}
Joe Tsaif0c01e42018-11-06 13:05:20 -0800144
145// TODO: The legacyExtensionInterfaceOf and legacyExtensionValueOf converters
146// exist since the current storage representation in the v1 data structures use
147// *T for scalars and []T for repeated fields, but the v2 API operates on
148// T for scalars and *[]T for repeated fields.
149//
150// Instead of maintaining this technical debt in the v2 repository,
151// we can offload this into the v1 implementation such that it uses a
152// storage representation that is appropriate for v2, and uses the these
153// functions to present the illusion that that the underlying storage
154// is still *T and []T.
155//
156// See https://github.com/golang/protobuf/pull/746
157const hasPR746 = true
158
159// legacyExtensionInterfaceOf converts a protoreflect.Value to the
160// storage representation used in v1 extension data structures.
161//
162// In particular, it represents scalars (except []byte) a pointer to the value,
163// and repeated fields as the a slice value itself.
164func legacyExtensionInterfaceOf(pv pref.Value, t pref.ExtensionType) interface{} {
165 v := t.InterfaceOf(pv)
166 if !hasPR746 {
167 switch rv := reflect.ValueOf(v); rv.Kind() {
168 case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
169 // Represent primitive types as a pointer to the value.
170 rv2 := reflect.New(rv.Type())
171 rv2.Elem().Set(rv)
172 v = rv2.Interface()
173 case reflect.Ptr:
174 // Represent pointer to slice types as the value itself.
175 switch rv.Type().Elem().Kind() {
176 case reflect.Slice:
177 if rv.IsNil() {
178 v = reflect.Zero(rv.Type().Elem()).Interface()
179 } else {
180 v = rv.Elem().Interface()
181 }
182 }
183 }
184 }
185 return v
186}
187
188// legacyExtensionValueOf converts the storage representation of a value in
189// the v1 extension data structures to a protoreflect.Value.
190//
191// In particular, it represents scalars as the value itself,
192// and repeated fields as a pointer to the slice value.
193func legacyExtensionValueOf(v interface{}, t pref.ExtensionType) pref.Value {
194 if !hasPR746 {
195 switch rv := reflect.ValueOf(v); rv.Kind() {
196 case reflect.Ptr:
197 // Represent slice types as the value itself.
198 switch rv.Type().Elem().Kind() {
199 case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
200 if rv.IsNil() {
201 v = reflect.Zero(rv.Type().Elem()).Interface()
202 } else {
203 v = rv.Elem().Interface()
204 }
205 }
206 case reflect.Slice:
207 // Represent slice types (except []byte) as a pointer to the value.
208 if rv.Type().Elem().Kind() != reflect.Uint8 {
209 rv2 := reflect.New(rv.Type())
210 rv2.Elem().Set(rv)
211 v = rv2.Interface()
212 }
213 }
214 }
215 return t.ValueOf(v)
216}