internal/impl: allow reflection on typed nil pointers
Similar to how generated messages allow you to call Get methods on a
nil pointer, we permit similar functionality when protobuf reflection
is used on a nil pointer.
Change-Id: Ie2f596d39105c191073b42d7d689525c3b715240
Reviewed-on: https://go-review.googlesource.com/c/152021
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/value/list.go b/internal/value/list.go
index dadb05f..bcac98b 100644
--- a/internal/value/list.go
+++ b/internal/value/list.go
@@ -15,41 +15,44 @@
Unwrapper
} {
// TODO: Validate that p is a *[]T?
- rv := reflect.ValueOf(p).Elem()
+ rv := reflect.ValueOf(p)
return listReflect{rv, c}
}
type listReflect struct {
- v reflect.Value // addressable []T
+ v reflect.Value // *[]T
conv Converter
}
func (ls listReflect) Len() int {
- return ls.v.Len()
+ if ls.v.IsNil() {
+ return 0
+ }
+ return ls.v.Elem().Len()
}
func (ls listReflect) Get(i int) pref.Value {
- return ls.conv.PBValueOf(ls.v.Index(i))
+ return ls.conv.PBValueOf(ls.v.Elem().Index(i))
}
func (ls listReflect) Set(i int, v pref.Value) {
- ls.v.Index(i).Set(ls.conv.GoValueOf(v))
+ ls.v.Elem().Index(i).Set(ls.conv.GoValueOf(v))
}
func (ls listReflect) Append(v pref.Value) {
- ls.v.Set(reflect.Append(ls.v, ls.conv.GoValueOf(v)))
+ ls.v.Elem().Set(reflect.Append(ls.v.Elem(), ls.conv.GoValueOf(v)))
}
func (ls listReflect) Mutable(i int) pref.Mutable {
// Mutable is only valid for messages and panics for other kinds.
- return ls.conv.PBValueOf(ls.v.Index(i)).Message()
+ return ls.conv.PBValueOf(ls.v.Elem().Index(i)).Message()
}
func (ls listReflect) MutableAppend() pref.Mutable {
// MutableAppend is only valid for messages and panics for other kinds.
pv := pref.ValueOf(ls.conv.MessageType.New().ProtoReflect())
- ls.v.Set(reflect.Append(ls.v, ls.conv.GoValueOf(pv)))
+ ls.v.Elem().Set(reflect.Append(ls.v.Elem(), ls.conv.GoValueOf(pv)))
return pv.Message()
}
func (ls listReflect) Truncate(i int) {
- ls.v.Set(ls.v.Slice(0, i))
+ ls.v.Elem().Set(ls.v.Elem().Slice(0, i))
}
func (ls listReflect) ProtoUnwrap() interface{} {
- return ls.v.Addr().Interface()
+ return ls.v.Interface()
}
func (ls listReflect) ProtoMutable() {}
diff --git a/internal/value/map.go b/internal/value/map.go
index 8505798..28b901b 100644
--- a/internal/value/map.go
+++ b/internal/value/map.go
@@ -15,61 +15,73 @@
Unwrapper
} {
// TODO: Validate that p is a *map[K]V?
- rv := reflect.ValueOf(p).Elem()
+ rv := reflect.ValueOf(p)
return mapReflect{rv, kc, kv}
}
type mapReflect struct {
- v reflect.Value // addressable map[K]V
+ v reflect.Value // *map[K]V
keyConv Converter
valConv Converter
}
func (ms mapReflect) Len() int {
- return ms.v.Len()
+ if ms.v.IsNil() {
+ return 0
+ }
+ return ms.v.Elem().Len()
}
func (ms mapReflect) Has(k pref.MapKey) bool {
+ if ms.v.IsNil() {
+ return false
+ }
rk := ms.keyConv.GoValueOf(k.Value())
- rv := ms.v.MapIndex(rk)
+ rv := ms.v.Elem().MapIndex(rk)
return rv.IsValid()
}
func (ms mapReflect) Get(k pref.MapKey) pref.Value {
+ if ms.v.IsNil() {
+ return pref.Value{}
+ }
rk := ms.keyConv.GoValueOf(k.Value())
- rv := ms.v.MapIndex(rk)
+ rv := ms.v.Elem().MapIndex(rk)
if !rv.IsValid() {
return pref.Value{}
}
return ms.valConv.PBValueOf(rv)
}
func (ms mapReflect) Set(k pref.MapKey, v pref.Value) {
- if ms.v.IsNil() {
- ms.v.Set(reflect.MakeMap(ms.v.Type()))
+ if ms.v.Elem().IsNil() {
+ ms.v.Elem().Set(reflect.MakeMap(ms.v.Elem().Type()))
}
rk := ms.keyConv.GoValueOf(k.Value())
rv := ms.valConv.GoValueOf(v)
- ms.v.SetMapIndex(rk, rv)
+ ms.v.Elem().SetMapIndex(rk, rv)
}
func (ms mapReflect) Clear(k pref.MapKey) {
rk := ms.keyConv.GoValueOf(k.Value())
- ms.v.SetMapIndex(rk, reflect.Value{})
+ ms.v.Elem().SetMapIndex(rk, reflect.Value{})
}
func (ms mapReflect) Mutable(k pref.MapKey) pref.Mutable {
// Mutable is only valid for messages and panics for other kinds.
- if ms.v.IsNil() {
- ms.v.Set(reflect.MakeMap(ms.v.Type()))
+ if ms.v.Elem().IsNil() {
+ ms.v.Elem().Set(reflect.MakeMap(ms.v.Elem().Type()))
}
rk := ms.keyConv.GoValueOf(k.Value())
- rv := ms.v.MapIndex(rk)
+ rv := ms.v.Elem().MapIndex(rk)
if !rv.IsValid() {
pv := pref.ValueOf(ms.valConv.MessageType.New().ProtoReflect())
rv = ms.valConv.GoValueOf(pv)
- ms.v.SetMapIndex(rk, rv)
+ ms.v.Elem().SetMapIndex(rk, rv)
}
return ms.valConv.PBValueOf(rv).Message()
}
func (ms mapReflect) Range(f func(pref.MapKey, pref.Value) bool) {
- for _, k := range ms.v.MapKeys() {
- if v := ms.v.MapIndex(k); v.IsValid() {
+ if ms.v.IsNil() {
+ return
+ }
+ for _, k := range ms.v.Elem().MapKeys() {
+ if v := ms.v.Elem().MapIndex(k); v.IsValid() {
pk := ms.keyConv.PBValueOf(k).MapKey()
pv := ms.valConv.PBValueOf(v)
if !f(pk, pv) {
@@ -79,6 +91,6 @@
}
}
func (ms mapReflect) ProtoUnwrap() interface{} {
- return ms.v.Addr().Interface()
+ return ms.v.Interface()
}
func (ms mapReflect) ProtoMutable() {}