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