Joe Tsai | fa02f4e | 2018-09-12 16:20:37 -0700 | [diff] [blame] | 1 | // 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 | |
| 5 | package impl |
| 6 | |
| 7 | import ( |
| 8 | "fmt" |
| 9 | "reflect" |
| 10 | |
Joe Tsai | 01ab296 | 2018-09-21 17:44:00 -0700 | [diff] [blame] | 11 | "github.com/golang/protobuf/v2/internal/flags" |
Joe Tsai | f0c01e4 | 2018-11-06 13:05:20 -0800 | [diff] [blame] | 12 | pvalue "github.com/golang/protobuf/v2/internal/value" |
Joe Tsai | 01ab296 | 2018-09-21 17:44:00 -0700 | [diff] [blame] | 13 | pref "github.com/golang/protobuf/v2/reflect/protoreflect" |
Joe Tsai | fa02f4e | 2018-09-12 16:20:37 -0700 | [diff] [blame] | 14 | ) |
| 15 | |
| 16 | type fieldInfo struct { |
| 17 | // TODO: specialize marshal and unmarshal functions? |
| 18 | |
| 19 | has func(pointer) bool |
| 20 | get func(pointer) pref.Value |
| 21 | set func(pointer, pref.Value) |
| 22 | clear func(pointer) |
| 23 | mutable func(pointer) pref.Mutable |
| 24 | } |
| 25 | |
| 26 | func fieldInfoForWeak(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo { |
| 27 | if !flags.Proto1Legacy { |
| 28 | panic("weak fields not supported") |
| 29 | } |
| 30 | // TODO: support weak fields. |
| 31 | panic(fmt.Sprintf("invalid field: %v", fd)) |
| 32 | } |
| 33 | |
| 34 | func fieldInfoForOneof(fd pref.FieldDescriptor, fs reflect.StructField, ot reflect.Type) fieldInfo { |
Joe Tsai | 2c870bb | 2018-10-17 11:46:52 -0700 | [diff] [blame] | 35 | ft := fs.Type |
| 36 | if ft.Kind() != reflect.Interface { |
| 37 | panic(fmt.Sprintf("invalid type: got %v, want interface kind", ft)) |
| 38 | } |
| 39 | if ot.Kind() != reflect.Struct { |
| 40 | panic(fmt.Sprintf("invalid type: got %v, want struct kind", ot)) |
| 41 | } |
| 42 | if !reflect.PtrTo(ot).Implements(ft) { |
| 43 | panic(fmt.Sprintf("invalid type: %v does not implement %v", ot, ft)) |
| 44 | } |
Joe Tsai | f0c01e4 | 2018-11-06 13:05:20 -0800 | [diff] [blame] | 45 | conv := newConverter(ot.Field(0).Type, fd.Kind()) |
Joe Tsai | 2c870bb | 2018-10-17 11:46:52 -0700 | [diff] [blame] | 46 | fieldOffset := offsetOf(fs) |
| 47 | // TODO: Implement unsafe fast path? |
| 48 | return fieldInfo{ |
| 49 | // NOTE: The logic below intentionally assumes that oneof fields are |
| 50 | // well-formatted. That is, the oneof interface never contains a |
| 51 | // typed nil pointer to one of the wrapper structs. |
| 52 | |
| 53 | has: func(p pointer) bool { |
| 54 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
| 55 | if rv.IsNil() || rv.Elem().Type().Elem() != ot { |
| 56 | return false |
| 57 | } |
| 58 | return true |
| 59 | }, |
| 60 | get: func(p pointer) pref.Value { |
| 61 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
| 62 | if rv.IsNil() || rv.Elem().Type().Elem() != ot { |
| 63 | if fd.Kind() == pref.MessageKind || fd.Kind() == pref.GroupKind { |
Joe Tsai | f6d4a42 | 2018-11-19 14:26:06 -0800 | [diff] [blame^] | 64 | return pref.Value{} |
Joe Tsai | 2c870bb | 2018-10-17 11:46:52 -0700 | [diff] [blame] | 65 | } |
| 66 | return fd.Default() |
| 67 | } |
| 68 | rv = rv.Elem().Elem().Field(0) |
Joe Tsai | 88bc5a7 | 2018-11-05 11:42:22 -0800 | [diff] [blame] | 69 | return conv.PBValueOf(rv) |
Joe Tsai | 2c870bb | 2018-10-17 11:46:52 -0700 | [diff] [blame] | 70 | }, |
| 71 | set: func(p pointer, v pref.Value) { |
| 72 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
| 73 | if rv.IsNil() || rv.Elem().Type().Elem() != ot { |
| 74 | rv.Set(reflect.New(ot)) |
| 75 | } |
| 76 | rv = rv.Elem().Elem().Field(0) |
Joe Tsai | 88bc5a7 | 2018-11-05 11:42:22 -0800 | [diff] [blame] | 77 | rv.Set(conv.GoValueOf(v)) |
Joe Tsai | 2c870bb | 2018-10-17 11:46:52 -0700 | [diff] [blame] | 78 | }, |
| 79 | clear: func(p pointer) { |
| 80 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
| 81 | if rv.IsNil() || rv.Elem().Type().Elem() != ot { |
| 82 | return |
| 83 | } |
| 84 | rv.Set(reflect.Zero(rv.Type())) |
| 85 | }, |
| 86 | mutable: func(p pointer) pref.Mutable { |
| 87 | // Mutable is only valid for messages and panics for other kinds. |
| 88 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
| 89 | if rv.IsNil() || rv.Elem().Type().Elem() != ot { |
| 90 | rv.Set(reflect.New(ot)) |
| 91 | } |
| 92 | rv = rv.Elem().Elem().Field(0) |
| 93 | if rv.IsNil() { |
Joe Tsai | 6f9095c | 2018-11-10 14:12:21 -0800 | [diff] [blame] | 94 | pv := pref.ValueOf(conv.MessageType.New().ProtoReflect()) |
Joe Tsai | 88bc5a7 | 2018-11-05 11:42:22 -0800 | [diff] [blame] | 95 | rv.Set(conv.GoValueOf(pv)) |
Joe Tsai | 2c870bb | 2018-10-17 11:46:52 -0700 | [diff] [blame] | 96 | } |
Joe Tsai | f6d4a42 | 2018-11-19 14:26:06 -0800 | [diff] [blame^] | 97 | return conv.PBValueOf(rv).Message() |
Joe Tsai | 2c870bb | 2018-10-17 11:46:52 -0700 | [diff] [blame] | 98 | }, |
| 99 | } |
Joe Tsai | fa02f4e | 2018-09-12 16:20:37 -0700 | [diff] [blame] | 100 | } |
| 101 | |
| 102 | func fieldInfoForMap(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo { |
Joe Tsai | bbfaeb7 | 2018-10-17 00:27:21 +0000 | [diff] [blame] | 103 | ft := fs.Type |
| 104 | if ft.Kind() != reflect.Map { |
| 105 | panic(fmt.Sprintf("invalid type: got %v, want map kind", ft)) |
| 106 | } |
Joe Tsai | f0c01e4 | 2018-11-06 13:05:20 -0800 | [diff] [blame] | 107 | keyConv := newConverter(ft.Key(), fd.MessageType().Fields().ByNumber(1).Kind()) |
| 108 | valConv := newConverter(ft.Elem(), fd.MessageType().Fields().ByNumber(2).Kind()) |
Joe Tsai | bbfaeb7 | 2018-10-17 00:27:21 +0000 | [diff] [blame] | 109 | fieldOffset := offsetOf(fs) |
| 110 | // TODO: Implement unsafe fast path? |
| 111 | return fieldInfo{ |
| 112 | has: func(p pointer) bool { |
| 113 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
| 114 | return rv.Len() > 0 |
| 115 | }, |
| 116 | get: func(p pointer) pref.Value { |
Joe Tsai | 88bc5a7 | 2018-11-05 11:42:22 -0800 | [diff] [blame] | 117 | v := p.apply(fieldOffset).asType(fs.Type).Interface() |
Joe Tsai | f0c01e4 | 2018-11-06 13:05:20 -0800 | [diff] [blame] | 118 | return pref.ValueOf(pvalue.MapOf(v, keyConv, valConv)) |
Joe Tsai | bbfaeb7 | 2018-10-17 00:27:21 +0000 | [diff] [blame] | 119 | }, |
| 120 | set: func(p pointer, v pref.Value) { |
| 121 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
Joe Tsai | f0c01e4 | 2018-11-06 13:05:20 -0800 | [diff] [blame] | 122 | rv.Set(reflect.ValueOf(v.Map().(pvalue.Unwrapper).Unwrap()).Elem()) |
Joe Tsai | bbfaeb7 | 2018-10-17 00:27:21 +0000 | [diff] [blame] | 123 | }, |
| 124 | clear: func(p pointer) { |
| 125 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
| 126 | rv.Set(reflect.Zero(rv.Type())) |
| 127 | }, |
| 128 | mutable: func(p pointer) pref.Mutable { |
Joe Tsai | 88bc5a7 | 2018-11-05 11:42:22 -0800 | [diff] [blame] | 129 | v := p.apply(fieldOffset).asType(fs.Type).Interface() |
Joe Tsai | f0c01e4 | 2018-11-06 13:05:20 -0800 | [diff] [blame] | 130 | return pvalue.MapOf(v, keyConv, valConv) |
Joe Tsai | bbfaeb7 | 2018-10-17 00:27:21 +0000 | [diff] [blame] | 131 | }, |
| 132 | } |
Joe Tsai | fa02f4e | 2018-09-12 16:20:37 -0700 | [diff] [blame] | 133 | } |
| 134 | |
Joe Tsai | 4b7aff6 | 2018-11-14 14:05:19 -0800 | [diff] [blame] | 135 | func fieldInfoForList(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo { |
Joe Tsai | 91e1466 | 2018-09-13 13:24:35 -0700 | [diff] [blame] | 136 | ft := fs.Type |
| 137 | if ft.Kind() != reflect.Slice { |
| 138 | panic(fmt.Sprintf("invalid type: got %v, want slice kind", ft)) |
| 139 | } |
Joe Tsai | f0c01e4 | 2018-11-06 13:05:20 -0800 | [diff] [blame] | 140 | conv := newConverter(ft.Elem(), fd.Kind()) |
Joe Tsai | 91e1466 | 2018-09-13 13:24:35 -0700 | [diff] [blame] | 141 | fieldOffset := offsetOf(fs) |
| 142 | // TODO: Implement unsafe fast path? |
| 143 | return fieldInfo{ |
| 144 | has: func(p pointer) bool { |
| 145 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
| 146 | return rv.Len() > 0 |
| 147 | }, |
| 148 | get: func(p pointer) pref.Value { |
Joe Tsai | 88bc5a7 | 2018-11-05 11:42:22 -0800 | [diff] [blame] | 149 | v := p.apply(fieldOffset).asType(fs.Type).Interface() |
Joe Tsai | 4b7aff6 | 2018-11-14 14:05:19 -0800 | [diff] [blame] | 150 | return pref.ValueOf(pvalue.ListOf(v, conv)) |
Joe Tsai | 91e1466 | 2018-09-13 13:24:35 -0700 | [diff] [blame] | 151 | }, |
| 152 | set: func(p pointer, v pref.Value) { |
| 153 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
Joe Tsai | 4b7aff6 | 2018-11-14 14:05:19 -0800 | [diff] [blame] | 154 | rv.Set(reflect.ValueOf(v.List().(pvalue.Unwrapper).Unwrap()).Elem()) |
Joe Tsai | 91e1466 | 2018-09-13 13:24:35 -0700 | [diff] [blame] | 155 | }, |
| 156 | clear: func(p pointer) { |
| 157 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
| 158 | rv.Set(reflect.Zero(rv.Type())) |
| 159 | }, |
| 160 | mutable: func(p pointer) pref.Mutable { |
Joe Tsai | 88bc5a7 | 2018-11-05 11:42:22 -0800 | [diff] [blame] | 161 | v := p.apply(fieldOffset).asType(fs.Type).Interface() |
Joe Tsai | 4b7aff6 | 2018-11-14 14:05:19 -0800 | [diff] [blame] | 162 | return pvalue.ListOf(v, conv) |
Joe Tsai | 91e1466 | 2018-09-13 13:24:35 -0700 | [diff] [blame] | 163 | }, |
| 164 | } |
Joe Tsai | fa02f4e | 2018-09-12 16:20:37 -0700 | [diff] [blame] | 165 | } |
| 166 | |
| 167 | var emptyBytes = reflect.ValueOf([]byte{}) |
| 168 | |
| 169 | func fieldInfoForScalar(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo { |
| 170 | ft := fs.Type |
| 171 | nullable := fd.Syntax() == pref.Proto2 |
| 172 | if nullable { |
| 173 | if ft.Kind() != reflect.Ptr && ft.Kind() != reflect.Slice { |
| 174 | panic(fmt.Sprintf("invalid type: got %v, want pointer", ft)) |
| 175 | } |
| 176 | if ft.Kind() == reflect.Ptr { |
| 177 | ft = ft.Elem() |
| 178 | } |
| 179 | } |
Joe Tsai | f0c01e4 | 2018-11-06 13:05:20 -0800 | [diff] [blame] | 180 | conv := newConverter(ft, fd.Kind()) |
Joe Tsai | fa02f4e | 2018-09-12 16:20:37 -0700 | [diff] [blame] | 181 | fieldOffset := offsetOf(fs) |
| 182 | // TODO: Implement unsafe fast path? |
| 183 | return fieldInfo{ |
| 184 | has: func(p pointer) bool { |
| 185 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
| 186 | if nullable { |
| 187 | return !rv.IsNil() |
| 188 | } |
| 189 | switch rv.Kind() { |
| 190 | case reflect.Bool: |
| 191 | return rv.Bool() |
| 192 | case reflect.Int32, reflect.Int64: |
Joe Tsai | 44e389c | 2018-11-19 15:27:09 -0800 | [diff] [blame] | 193 | return rv.Int() != 0 |
Joe Tsai | fa02f4e | 2018-09-12 16:20:37 -0700 | [diff] [blame] | 194 | case reflect.Uint32, reflect.Uint64: |
Joe Tsai | 44e389c | 2018-11-19 15:27:09 -0800 | [diff] [blame] | 195 | return rv.Uint() != 0 |
Joe Tsai | fa02f4e | 2018-09-12 16:20:37 -0700 | [diff] [blame] | 196 | case reflect.Float32, reflect.Float64: |
Joe Tsai | 44e389c | 2018-11-19 15:27:09 -0800 | [diff] [blame] | 197 | return rv.Float() != 0 |
Joe Tsai | fa02f4e | 2018-09-12 16:20:37 -0700 | [diff] [blame] | 198 | case reflect.String, reflect.Slice: |
| 199 | return rv.Len() > 0 |
| 200 | default: |
| 201 | panic(fmt.Sprintf("invalid type: %v", rv.Type())) // should never happen |
| 202 | } |
| 203 | }, |
| 204 | get: func(p pointer) pref.Value { |
| 205 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
| 206 | if nullable { |
| 207 | if rv.IsNil() { |
| 208 | pv := fd.Default() |
| 209 | if fd.Kind() == pref.BytesKind && len(pv.Bytes()) > 0 { |
| 210 | return pref.ValueOf(append([]byte(nil), pv.Bytes()...)) // copy default bytes for safety |
| 211 | } |
| 212 | return pv |
| 213 | } |
| 214 | if rv.Kind() == reflect.Ptr { |
| 215 | rv = rv.Elem() |
| 216 | } |
| 217 | } |
Joe Tsai | 88bc5a7 | 2018-11-05 11:42:22 -0800 | [diff] [blame] | 218 | return conv.PBValueOf(rv) |
Joe Tsai | fa02f4e | 2018-09-12 16:20:37 -0700 | [diff] [blame] | 219 | }, |
| 220 | set: func(p pointer, v pref.Value) { |
| 221 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
| 222 | if nullable && rv.Kind() == reflect.Ptr { |
| 223 | if rv.IsNil() { |
| 224 | rv.Set(reflect.New(ft)) |
| 225 | } |
| 226 | rv = rv.Elem() |
| 227 | } |
Joe Tsai | 88bc5a7 | 2018-11-05 11:42:22 -0800 | [diff] [blame] | 228 | rv.Set(conv.GoValueOf(v)) |
Joe Tsai | fa02f4e | 2018-09-12 16:20:37 -0700 | [diff] [blame] | 229 | if nullable && rv.Kind() == reflect.Slice && rv.IsNil() { |
| 230 | rv.Set(emptyBytes) |
| 231 | } |
| 232 | }, |
| 233 | clear: func(p pointer) { |
| 234 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
| 235 | rv.Set(reflect.Zero(rv.Type())) |
| 236 | }, |
| 237 | mutable: func(p pointer) pref.Mutable { |
| 238 | panic("invalid mutable call") |
| 239 | }, |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | func fieldInfoForMessage(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo { |
Joe Tsai | ce6edd3 | 2018-10-19 16:27:46 -0700 | [diff] [blame] | 244 | ft := fs.Type |
Joe Tsai | f0c01e4 | 2018-11-06 13:05:20 -0800 | [diff] [blame] | 245 | conv := newConverter(ft, fd.Kind()) |
Joe Tsai | ce6edd3 | 2018-10-19 16:27:46 -0700 | [diff] [blame] | 246 | fieldOffset := offsetOf(fs) |
| 247 | // TODO: Implement unsafe fast path? |
| 248 | return fieldInfo{ |
| 249 | has: func(p pointer) bool { |
| 250 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
| 251 | return !rv.IsNil() |
| 252 | }, |
| 253 | get: func(p pointer) pref.Value { |
Joe Tsai | ce6edd3 | 2018-10-19 16:27:46 -0700 | [diff] [blame] | 254 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
Joe Tsai | f6d4a42 | 2018-11-19 14:26:06 -0800 | [diff] [blame^] | 255 | if rv.IsNil() { |
| 256 | return pref.Value{} |
| 257 | } |
Joe Tsai | 88bc5a7 | 2018-11-05 11:42:22 -0800 | [diff] [blame] | 258 | return conv.PBValueOf(rv) |
Joe Tsai | ce6edd3 | 2018-10-19 16:27:46 -0700 | [diff] [blame] | 259 | }, |
| 260 | set: func(p pointer, v pref.Value) { |
Joe Tsai | ce6edd3 | 2018-10-19 16:27:46 -0700 | [diff] [blame] | 261 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
Joe Tsai | 88bc5a7 | 2018-11-05 11:42:22 -0800 | [diff] [blame] | 262 | rv.Set(conv.GoValueOf(v)) |
Joe Tsai | f6d4a42 | 2018-11-19 14:26:06 -0800 | [diff] [blame^] | 263 | if rv.IsNil() { |
| 264 | panic("invalid nil pointer") |
| 265 | } |
Joe Tsai | ce6edd3 | 2018-10-19 16:27:46 -0700 | [diff] [blame] | 266 | }, |
| 267 | clear: func(p pointer) { |
| 268 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
| 269 | rv.Set(reflect.Zero(rv.Type())) |
| 270 | }, |
| 271 | mutable: func(p pointer) pref.Mutable { |
| 272 | // Mutable is only valid for messages and panics for other kinds. |
| 273 | rv := p.apply(fieldOffset).asType(fs.Type).Elem() |
| 274 | if rv.IsNil() { |
Joe Tsai | 6f9095c | 2018-11-10 14:12:21 -0800 | [diff] [blame] | 275 | pv := pref.ValueOf(conv.MessageType.New().ProtoReflect()) |
Joe Tsai | 88bc5a7 | 2018-11-05 11:42:22 -0800 | [diff] [blame] | 276 | rv.Set(conv.GoValueOf(pv)) |
Joe Tsai | ce6edd3 | 2018-10-19 16:27:46 -0700 | [diff] [blame] | 277 | } |
Joe Tsai | 88bc5a7 | 2018-11-05 11:42:22 -0800 | [diff] [blame] | 278 | return conv.PBValueOf(rv).Message() |
Joe Tsai | fa02f4e | 2018-09-12 16:20:37 -0700 | [diff] [blame] | 279 | }, |
| 280 | } |
| 281 | } |
Joe Tsai | f0c01e4 | 2018-11-06 13:05:20 -0800 | [diff] [blame] | 282 | |
| 283 | // newConverter calls value.NewLegacyConverter with the necessary constructor |
| 284 | // functions for legacy enum and message support. |
| 285 | func newConverter(t reflect.Type, k pref.Kind) pvalue.Converter { |
| 286 | messageType := func(t reflect.Type) pref.MessageType { |
| 287 | return legacyLoadMessageType(t).Type |
| 288 | } |
| 289 | return pvalue.NewLegacyConverter(t, k, legacyLoadEnumType, messageType, legacyWrapMessage) |
| 290 | } |