mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-03-15 22:20:52 +00:00
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>
This commit is contained in:
parent
f9123a3cd0
commit
6cf80c4f76
@ -17,6 +17,9 @@ func makeLegacyExtensionFieldsFunc(t reflect.Type) func(p *messageDataType) pref
|
||||
return nil
|
||||
}
|
||||
return func(p *messageDataType) pref.KnownFields {
|
||||
if p.p.IsNil() {
|
||||
return emptyExtensionFields{}
|
||||
}
|
||||
return legacyExtensionFields{p.mi, f(p)}
|
||||
}
|
||||
}
|
||||
@ -33,13 +36,13 @@ func makeLegacyExtensionMapFunc(t reflect.Type) func(*messageDataType) papi.Exte
|
||||
case fx1.Type == extTypeA:
|
||||
fieldOffset := offsetOf(fx1)
|
||||
return func(p *messageDataType) papi.ExtensionFields {
|
||||
v := p.p.apply(fieldOffset).asType(fx1.Type).Interface()
|
||||
v := p.p.Apply(fieldOffset).AsValueOf(fx1.Type).Interface()
|
||||
return papi.ExtensionFieldsOf(v)
|
||||
}
|
||||
case fx2.Type == extTypeB:
|
||||
fieldOffset := offsetOf(fx2)
|
||||
return func(p *messageDataType) papi.ExtensionFields {
|
||||
v := p.p.apply(fieldOffset).asType(fx2.Type).Interface()
|
||||
v := p.p.Apply(fieldOffset).AsValueOf(fx2.Type).Interface()
|
||||
return papi.ExtensionFieldsOf(v)
|
||||
}
|
||||
default:
|
||||
|
@ -23,12 +23,18 @@ func makeLegacyUnknownFieldsFunc(t reflect.Type) func(p *messageDataType) pref.U
|
||||
}
|
||||
fieldOffset := offsetOf(fu)
|
||||
unkFunc := func(p *messageDataType) pref.UnknownFields {
|
||||
rv := p.p.apply(fieldOffset).asType(bytesType)
|
||||
if p.p.IsNil() {
|
||||
return emptyUnknownFields{}
|
||||
}
|
||||
rv := p.p.Apply(fieldOffset).AsValueOf(bytesType)
|
||||
return (*legacyUnknownBytes)(rv.Interface().(*[]byte))
|
||||
}
|
||||
extFunc := makeLegacyExtensionMapFunc(t)
|
||||
if extFunc != nil {
|
||||
return func(p *messageDataType) pref.UnknownFields {
|
||||
if p.p.IsNil() {
|
||||
return emptyUnknownFields{}
|
||||
}
|
||||
return &legacyUnknownBytesAndExtensionMap{
|
||||
unkFunc(p), extFunc(p), p.mi.Type.ExtensionRanges(),
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ func (mi *MessageType) UnknownFieldsOf(p interface{}) pref.UnknownFields {
|
||||
|
||||
func (mi *MessageType) dataTypeOf(p interface{}) *messageDataType {
|
||||
mi.init(p)
|
||||
return &messageDataType{pointerOfIface(&p), mi}
|
||||
return &messageDataType{pointerOfIface(p), mi}
|
||||
}
|
||||
|
||||
// messageDataType is a tuple of a pointer to the message data and
|
||||
@ -216,7 +216,7 @@ func (m *messageWrapper) ProtoReflect() pref.Message {
|
||||
return m
|
||||
}
|
||||
func (m *messageWrapper) ProtoUnwrap() interface{} {
|
||||
return m.p.asType(m.mi.goType.Elem()).Interface()
|
||||
return m.p.AsIfaceOf(m.mi.goType.Elem())
|
||||
}
|
||||
func (m *messageWrapper) ProtoMutable() {}
|
||||
|
||||
|
@ -51,25 +51,28 @@ func fieldInfoForOneof(fd pref.FieldDescriptor, fs reflect.StructField, ot refle
|
||||
// typed nil pointer to one of the wrapper structs.
|
||||
|
||||
has: func(p pointer) bool {
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
if p.IsNil() {
|
||||
return false
|
||||
}
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
if rv.IsNil() || rv.Elem().Type().Elem() != ot {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
get: func(p pointer) pref.Value {
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
if p.IsNil() {
|
||||
return defaultValueOf(fd)
|
||||
}
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
if rv.IsNil() || rv.Elem().Type().Elem() != ot {
|
||||
if fd.Kind() == pref.MessageKind || fd.Kind() == pref.GroupKind {
|
||||
return pref.Value{}
|
||||
}
|
||||
return fd.Default()
|
||||
return defaultValueOf(fd)
|
||||
}
|
||||
rv = rv.Elem().Elem().Field(0)
|
||||
return conv.PBValueOf(rv)
|
||||
},
|
||||
set: func(p pointer, v pref.Value) {
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
if rv.IsNil() || rv.Elem().Type().Elem() != ot {
|
||||
rv.Set(reflect.New(ot))
|
||||
}
|
||||
@ -77,7 +80,7 @@ func fieldInfoForOneof(fd pref.FieldDescriptor, fs reflect.StructField, ot refle
|
||||
rv.Set(conv.GoValueOf(v))
|
||||
},
|
||||
clear: func(p pointer) {
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
if rv.IsNil() || rv.Elem().Type().Elem() != ot {
|
||||
return
|
||||
}
|
||||
@ -85,7 +88,7 @@ func fieldInfoForOneof(fd pref.FieldDescriptor, fs reflect.StructField, ot refle
|
||||
},
|
||||
mutable: func(p pointer) pref.Mutable {
|
||||
// Mutable is only valid for messages and panics for other kinds.
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
if rv.IsNil() || rv.Elem().Type().Elem() != ot {
|
||||
rv.Set(reflect.New(ot))
|
||||
}
|
||||
@ -110,23 +113,30 @@ func fieldInfoForMap(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo
|
||||
// TODO: Implement unsafe fast path?
|
||||
return fieldInfo{
|
||||
has: func(p pointer) bool {
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
if p.IsNil() {
|
||||
return false
|
||||
}
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
return rv.Len() > 0
|
||||
},
|
||||
get: func(p pointer) pref.Value {
|
||||
v := p.apply(fieldOffset).asType(fs.Type).Interface()
|
||||
if p.IsNil() {
|
||||
v := reflect.Zero(reflect.PtrTo(fs.Type)).Interface()
|
||||
return pref.ValueOf(pvalue.MapOf(v, keyConv, valConv))
|
||||
}
|
||||
v := p.Apply(fieldOffset).AsIfaceOf(fs.Type)
|
||||
return pref.ValueOf(pvalue.MapOf(v, keyConv, valConv))
|
||||
},
|
||||
set: func(p pointer, v pref.Value) {
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
rv.Set(reflect.ValueOf(v.Map().(pvalue.Unwrapper).ProtoUnwrap()).Elem())
|
||||
},
|
||||
clear: func(p pointer) {
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
rv.Set(reflect.Zero(rv.Type()))
|
||||
},
|
||||
mutable: func(p pointer) pref.Mutable {
|
||||
v := p.apply(fieldOffset).asType(fs.Type).Interface()
|
||||
v := p.Apply(fieldOffset).AsIfaceOf(fs.Type)
|
||||
return pvalue.MapOf(v, keyConv, valConv)
|
||||
},
|
||||
}
|
||||
@ -142,23 +152,30 @@ func fieldInfoForList(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo
|
||||
// TODO: Implement unsafe fast path?
|
||||
return fieldInfo{
|
||||
has: func(p pointer) bool {
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
if p.IsNil() {
|
||||
return false
|
||||
}
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
return rv.Len() > 0
|
||||
},
|
||||
get: func(p pointer) pref.Value {
|
||||
v := p.apply(fieldOffset).asType(fs.Type).Interface()
|
||||
if p.IsNil() {
|
||||
v := reflect.Zero(reflect.PtrTo(fs.Type)).Interface()
|
||||
return pref.ValueOf(pvalue.ListOf(v, conv))
|
||||
}
|
||||
v := p.Apply(fieldOffset).AsIfaceOf(fs.Type)
|
||||
return pref.ValueOf(pvalue.ListOf(v, conv))
|
||||
},
|
||||
set: func(p pointer, v pref.Value) {
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
rv.Set(reflect.ValueOf(v.List().(pvalue.Unwrapper).ProtoUnwrap()).Elem())
|
||||
},
|
||||
clear: func(p pointer) {
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
rv.Set(reflect.Zero(rv.Type()))
|
||||
},
|
||||
mutable: func(p pointer) pref.Mutable {
|
||||
v := p.apply(fieldOffset).asType(fs.Type).Interface()
|
||||
v := p.Apply(fieldOffset).AsIfaceOf(fs.Type)
|
||||
return pvalue.ListOf(v, conv)
|
||||
},
|
||||
}
|
||||
@ -182,7 +199,10 @@ func fieldInfoForScalar(fd pref.FieldDescriptor, fs reflect.StructField) fieldIn
|
||||
// TODO: Implement unsafe fast path?
|
||||
return fieldInfo{
|
||||
has: func(p pointer) bool {
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
if p.IsNil() {
|
||||
return false
|
||||
}
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
if nullable {
|
||||
return !rv.IsNil()
|
||||
}
|
||||
@ -202,14 +222,13 @@ func fieldInfoForScalar(fd pref.FieldDescriptor, fs reflect.StructField) fieldIn
|
||||
}
|
||||
},
|
||||
get: func(p pointer) pref.Value {
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
if p.IsNil() {
|
||||
return defaultValueOf(fd)
|
||||
}
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
if nullable {
|
||||
if rv.IsNil() {
|
||||
pv := fd.Default()
|
||||
if fd.Kind() == pref.BytesKind && len(pv.Bytes()) > 0 {
|
||||
return pref.ValueOf(append([]byte(nil), pv.Bytes()...)) // copy default bytes for safety
|
||||
}
|
||||
return pv
|
||||
return defaultValueOf(fd)
|
||||
}
|
||||
if rv.Kind() == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
@ -218,7 +237,7 @@ func fieldInfoForScalar(fd pref.FieldDescriptor, fs reflect.StructField) fieldIn
|
||||
return conv.PBValueOf(rv)
|
||||
},
|
||||
set: func(p pointer, v pref.Value) {
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
if nullable && rv.Kind() == reflect.Ptr {
|
||||
if rv.IsNil() {
|
||||
rv.Set(reflect.New(ft))
|
||||
@ -231,7 +250,7 @@ func fieldInfoForScalar(fd pref.FieldDescriptor, fs reflect.StructField) fieldIn
|
||||
}
|
||||
},
|
||||
clear: func(p pointer) {
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
rv.Set(reflect.Zero(rv.Type()))
|
||||
},
|
||||
mutable: func(p pointer) pref.Mutable {
|
||||
@ -247,30 +266,36 @@ func fieldInfoForMessage(fd pref.FieldDescriptor, fs reflect.StructField) fieldI
|
||||
// TODO: Implement unsafe fast path?
|
||||
return fieldInfo{
|
||||
has: func(p pointer) bool {
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
if p.IsNil() {
|
||||
return false
|
||||
}
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
return !rv.IsNil()
|
||||
},
|
||||
get: func(p pointer) pref.Value {
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
if p.IsNil() {
|
||||
return pref.Value{}
|
||||
}
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
if rv.IsNil() {
|
||||
return pref.Value{}
|
||||
}
|
||||
return conv.PBValueOf(rv)
|
||||
},
|
||||
set: func(p pointer, v pref.Value) {
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
rv.Set(conv.GoValueOf(v))
|
||||
if rv.IsNil() {
|
||||
panic("invalid nil pointer")
|
||||
}
|
||||
},
|
||||
clear: func(p pointer) {
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
rv.Set(reflect.Zero(rv.Type()))
|
||||
},
|
||||
mutable: func(p pointer) pref.Mutable {
|
||||
// Mutable is only valid for messages and panics for other kinds.
|
||||
rv := p.apply(fieldOffset).asType(fs.Type).Elem()
|
||||
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
||||
if rv.IsNil() {
|
||||
pv := pref.ValueOf(conv.MessageType.New().ProtoReflect())
|
||||
rv.Set(conv.GoValueOf(pv))
|
||||
@ -279,3 +304,15 @@ func fieldInfoForMessage(fd pref.FieldDescriptor, fs reflect.StructField) fieldI
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// defaultValueOf returns the default value for the field.
|
||||
func defaultValueOf(fd pref.FieldDescriptor) pref.Value {
|
||||
if fd == nil {
|
||||
return pref.Value{}
|
||||
}
|
||||
pv := fd.Default() // invalid Value for messages and repeated fields
|
||||
if fd.Kind() == pref.BytesKind && pv.IsValid() && len(pv.Bytes()) > 0 {
|
||||
return pref.ValueOf(append([]byte(nil), pv.Bytes()...)) // copy default bytes for safety
|
||||
}
|
||||
return pv
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
"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 @@ import (
|
||||
// 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 @@ func TestScalarProto2(t *testing.T) {
|
||||
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 @@ func TestScalarProto3(t *testing.T) {
|
||||
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 @@ func TestListScalars(t *testing.T) {
|
||||
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 @@ func mustMakeMapEntry(n pref.FieldNumber, keyKind, valKind pref.Kind) ptype.Fiel
|
||||
{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 @@ func TestMapScalars(t *testing.T) {
|
||||
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 @@ func TestOneofs(t *testing.T) {
|
||||
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 @@ var enumMapDesc = mustMakeMessageDesc(ptype.StandaloneMessage{
|
||||
{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 @@ var messageMapDesc = mustMakeMessageDesc(ptype.StandaloneMessage{
|
||||
{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 @@ func TestEnumMessages(t *testing.T) {
|
||||
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 @@ func testMessage(t *testing.T, p path, m pref.Message, tt messageOps) {
|
||||
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:
|
||||
|
@ -32,21 +32,33 @@ func pointerOfValue(v reflect.Value) pointer {
|
||||
}
|
||||
|
||||
// pointerOfIface returns the pointer portion of an interface.
|
||||
func pointerOfIface(v *interface{}) pointer {
|
||||
return pointer{v: reflect.ValueOf(*v)}
|
||||
func pointerOfIface(v interface{}) pointer {
|
||||
return pointer{v: reflect.ValueOf(v)}
|
||||
}
|
||||
|
||||
// apply adds an offset to the pointer to derive a new pointer
|
||||
// IsNil reports whether the pointer is nil.
|
||||
func (p pointer) IsNil() bool {
|
||||
return p.v.IsNil()
|
||||
}
|
||||
|
||||
// Apply adds an offset to the pointer to derive a new pointer
|
||||
// to a specified field. The current pointer must be pointing at a struct.
|
||||
func (p pointer) apply(f offset) pointer {
|
||||
func (p pointer) Apply(f offset) pointer {
|
||||
// TODO: Handle unexported fields in an API that hides XXX fields?
|
||||
return pointer{v: p.v.Elem().FieldByIndex(f).Addr()}
|
||||
}
|
||||
|
||||
// asType treats p as a pointer to an object of type t and returns the value.
|
||||
func (p pointer) asType(t reflect.Type) reflect.Value {
|
||||
// AsValueOf treats p as a pointer to an object of type t and returns the value.
|
||||
// It is equivalent to reflect.ValueOf(p.AsIfaceOf(t))
|
||||
func (p pointer) AsValueOf(t reflect.Type) reflect.Value {
|
||||
if p.v.Type().Elem() != t {
|
||||
panic(fmt.Sprintf("invalid type: got %v, want %v", p.v.Type(), t))
|
||||
}
|
||||
return p.v
|
||||
}
|
||||
|
||||
// AsIfaceOf treats p as a pointer to an object of type t and returns the value.
|
||||
// It is equivalent to p.AsValueOf(t).Interface()
|
||||
func (p pointer) AsIfaceOf(t reflect.Type) interface{} {
|
||||
return p.AsValueOf(t).Interface()
|
||||
}
|
||||
|
@ -29,21 +29,37 @@ func pointerOfValue(v reflect.Value) pointer {
|
||||
}
|
||||
|
||||
// pointerOfIface returns the pointer portion of an interface.
|
||||
func pointerOfIface(v *interface{}) pointer {
|
||||
func pointerOfIface(v interface{}) pointer {
|
||||
type ifaceHeader struct {
|
||||
Type unsafe.Pointer
|
||||
Data unsafe.Pointer
|
||||
}
|
||||
return pointer{p: (*ifaceHeader)(unsafe.Pointer(v)).Data}
|
||||
return pointer{p: (*ifaceHeader)(unsafe.Pointer(&v)).Data}
|
||||
}
|
||||
|
||||
// apply adds an offset to the pointer to derive a new pointer
|
||||
// to a specified field. The current pointer must be pointing at a struct.
|
||||
func (p pointer) apply(f offset) pointer {
|
||||
// IsNil reports whether the pointer is nil.
|
||||
func (p pointer) IsNil() bool {
|
||||
return p.p == nil
|
||||
}
|
||||
|
||||
// Apply adds an offset to the pointer to derive a new pointer
|
||||
// to a specified field. The pointer must be valid and pointing at a struct.
|
||||
func (p pointer) Apply(f offset) pointer {
|
||||
if p.IsNil() {
|
||||
panic("invalid nil pointer")
|
||||
}
|
||||
return pointer{p: unsafe.Pointer(uintptr(p.p) + uintptr(f))}
|
||||
}
|
||||
|
||||
// asType treats p as a pointer to an object of type t and returns the value.
|
||||
func (p pointer) asType(t reflect.Type) reflect.Value {
|
||||
// AsValueOf treats p as a pointer to an object of type t and returns the value.
|
||||
// It is equivalent to reflect.ValueOf(p.AsIfaceOf(t))
|
||||
func (p pointer) AsValueOf(t reflect.Type) reflect.Value {
|
||||
return reflect.NewAt(t, p.p)
|
||||
}
|
||||
|
||||
// AsIfaceOf treats p as a pointer to an object of type t and returns the value.
|
||||
// It is equivalent to p.AsValueOf(t).Interface()
|
||||
func (p pointer) AsIfaceOf(t reflect.Type) interface{} {
|
||||
// TODO: Use tricky unsafe magic to directly create ifaceHeader.
|
||||
return p.AsValueOf(t).Interface()
|
||||
}
|
||||
|
@ -15,41 +15,44 @@ func ListOf(p interface{}, c Converter) interface {
|
||||
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() {}
|
||||
|
@ -15,61 +15,73 @@ func MapOf(p interface{}, kc, kv Converter) interface {
|
||||
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) Range(f func(pref.MapKey, pref.Value) bool) {
|
||||
}
|
||||
}
|
||||
func (ms mapReflect) ProtoUnwrap() interface{} {
|
||||
return ms.v.Addr().Interface()
|
||||
return ms.v.Interface()
|
||||
}
|
||||
func (ms mapReflect) ProtoMutable() {}
|
||||
|
Loading…
x
Reference in New Issue
Block a user