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/impl/message_test.go b/internal/impl/message_test.go
index d388494..1f97430 100644
--- a/internal/impl/message_test.go
+++ b/internal/impl/message_test.go
@@ -11,7 +11,6 @@
"testing"
protoV1 "github.com/golang/protobuf/proto"
- descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
pimpl "github.com/golang/protobuf/v2/internal/impl"
scalar "github.com/golang/protobuf/v2/internal/scalar"
pvalue "github.com/golang/protobuf/v2/internal/value"
@@ -24,6 +23,7 @@
// TODO: Remove this when protoV1 registers these hooks for you.
_ "github.com/golang/protobuf/v2/internal/legacy"
+ descriptorpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
proto2_20180125 "github.com/golang/protobuf/v2/internal/testprotos/legacy/proto2.v1.0.0-20180125-92554152"
)
@@ -256,6 +256,18 @@
clearFields{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22},
equalMessage{&ScalarProto2{}},
})
+
+ // Test read-only operations on nil message.
+ testMessage(t, nil, (*ScalarProto2)(nil), messageOps{
+ hasFields{
+ 1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false, 11: false,
+ 12: false, 13: false, 14: false, 15: false, 16: false, 17: false, 18: false, 19: false, 20: false, 21: false, 22: false,
+ },
+ getFields{
+ 1: V(bool(true)), 2: V(int32(2)), 3: V(int64(3)), 4: V(uint32(4)), 5: V(uint64(5)), 6: V(float32(6)), 7: V(float64(7)), 8: V(string("8")), 9: V(string("9")), 10: V([]byte("10")), 11: V([]byte("11")),
+ 12: V(bool(true)), 13: V(int32(13)), 14: V(int64(14)), 15: V(uint32(15)), 16: V(uint64(16)), 17: V(float32(17)), 18: V(float64(18)), 19: V(string("19")), 20: V(string("20")), 21: V([]byte("21")), 22: V([]byte("22")),
+ },
+ })
}
type ScalarProto3 struct {
@@ -366,6 +378,18 @@
clearFields{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22},
equalMessage{&ScalarProto3{}},
})
+
+ // Test read-only operations on nil message.
+ testMessage(t, nil, (*ScalarProto3)(nil), messageOps{
+ hasFields{
+ 1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false, 11: false,
+ 12: false, 13: false, 14: false, 15: false, 16: false, 17: false, 18: false, 19: false, 20: false, 21: false, 22: false,
+ },
+ getFields{
+ 1: V(bool(false)), 2: V(int32(0)), 3: V(int64(0)), 4: V(uint32(0)), 5: V(uint64(0)), 6: V(float32(0)), 7: V(float64(0)), 8: V(string("")), 9: V(string("")), 10: V([]byte(nil)), 11: V([]byte(nil)),
+ 12: V(bool(false)), 13: V(int32(0)), 14: V(int64(0)), 15: V(uint32(0)), 16: V(uint64(0)), 17: V(float32(0)), 18: V(float64(0)), 19: V(string("")), 20: V(string("")), 21: V([]byte(nil)), 22: V([]byte(nil)),
+ },
+ })
}
type ListScalars struct {
@@ -519,6 +543,12 @@
clearFields{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19},
equalMessage{empty},
})
+
+ // Test read-only operations on nil message.
+ testMessage(t, nil, (*ListScalars)(nil), messageOps{
+ hasFields{1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false, 11: false, 12: false, 13: false, 14: false, 15: false, 16: false, 17: false, 18: false, 19: false},
+ listFields{2: {lenList(0)}, 4: {lenList(0)}, 6: {lenList(0)}, 8: {lenList(0)}, 10: {lenList(0)}, 12: {lenList(0)}, 14: {lenList(0)}, 16: {lenList(0)}, 18: {lenList(0)}},
+ })
}
type MapScalars struct {
@@ -565,7 +595,7 @@
{Name: "key", Number: 1, Cardinality: pref.Optional, Kind: keyKind},
{Name: "value", Number: 2, Cardinality: pref.Optional, Kind: valKind},
},
- Options: &descriptorV1.MessageOptions{MapEntry: scalar.Bool(true)},
+ Options: &descriptorpb.MessageOptions{MapEntry: scalar.Bool(true)},
}),
}
}
@@ -731,6 +761,12 @@
clearFields{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
equalMessage{empty},
})
+
+ // Test read-only operations on nil message.
+ testMessage(t, nil, (*MapScalars)(nil), messageOps{
+ hasFields{1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false, 11: false, 12: false, 13: false, 14: false, 15: false, 16: false, 17: false, 18: false, 19: false, 20: false, 21: false, 22: false, 23: false, 24: false, 25: false},
+ mapFields{2: {lenMap(0)}, 4: {lenMap(0)}, 6: {lenMap(0)}, 8: {lenMap(0)}, 10: {lenMap(0)}, 12: {lenMap(0)}, 14: {lenMap(0)}, 16: {lenMap(0)}, 18: {lenMap(0)}, 20: {lenMap(0)}, 22: {lenMap(0)}, 24: {lenMap(0)}},
+ })
}
type OneofScalars struct {
@@ -888,6 +924,12 @@
clearFields{13},
equalMessage{empty},
})
+
+ // Test read-only operations on nil message.
+ testMessage(t, nil, (*OneofScalars)(nil), messageOps{
+ hasFields{1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false, 11: false, 12: false, 13: false},
+ getFields{1: V(bool(true)), 2: V(int32(2)), 3: V(int64(3)), 4: V(uint32(4)), 5: V(uint64(5)), 6: V(float32(6)), 7: V(float64(7)), 8: V(string("8")), 9: V(string("9")), 10: V(string("10")), 11: V([]byte("11")), 12: V([]byte("12")), 13: V([]byte("13"))},
+ })
}
type EnumProto2 int32
@@ -970,7 +1012,7 @@
{Name: "key", Number: 1, Cardinality: pref.Optional, Kind: pref.StringKind},
{Name: "value", Number: 2, Cardinality: pref.Optional, Kind: pref.EnumKind, EnumType: enumProto3Type},
},
- Options: &descriptorV1.MessageOptions{MapEntry: scalar.Bool(true)},
+ Options: &descriptorpb.MessageOptions{MapEntry: scalar.Bool(true)},
})
var messageMapDesc = mustMakeMessageDesc(ptype.StandaloneMessage{
@@ -980,7 +1022,7 @@
{Name: "key", Number: 1, Cardinality: pref.Optional, Kind: pref.StringKind},
{Name: "value", Number: 2, Cardinality: pref.Optional, Kind: pref.MessageKind, MessageType: scalarProto3Type.Type},
},
- Options: &descriptorV1.MessageOptions{MapEntry: scalar.Bool(true)},
+ Options: &descriptorpb.MessageOptions{MapEntry: scalar.Bool(true)},
})
func (m *EnumMessages) Type() pref.MessageType { return enumMessagesType.Type }
@@ -1119,6 +1161,14 @@
clearFields{1, 2, 3, 4, 6, 7, 12},
equalMessage{&EnumMessages{}},
})
+
+ // Test read-only operations on nil message.
+ testMessage(t, nil, (*EnumMessages)(nil), messageOps{
+ hasFields{1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false, 11: false, 12: false},
+ getFields{1: VE(0xbeef), 2: VE(1), 3: V(nil), 4: V(nil), 9: VE(0xbeef), 10: VE(1), 11: V(nil), 12: V(nil)},
+ listFields{5: {lenList(0)}, 6: {lenList(0)}},
+ mapFields{7: {lenMap(0)}, 8: {lenMap(0)}},
+ })
}
var cmpOpts = cmp.Options{
@@ -1179,13 +1229,13 @@
case listFields:
for n, tt := range op {
p.Push(int(n))
- testLists(t, p, fs.Mutable(n).(pref.List), tt)
+ testLists(t, p, fs.Get(n).List(), tt)
p.Pop()
}
case mapFields:
for n, tt := range op {
p.Push(int(n))
- testMaps(t, p, fs.Mutable(n).(pref.Map), tt)
+ testMaps(t, p, fs.Get(n).Map(), tt)
p.Pop()
}
case rangeFields: