protobuf-go/internal/impl/convert_list.go
Joe Tsai b57aae9def all: improve extension validation
Changes made:
* Ensure protoreflect.ExtensionType.IsValidInterface never panics,
especially if given a nil interface value.
* Have protoreflect.ExtensionType.IsValid{Interface,Value} only
perform type-checks. It does not do value checks (i.e., whether the
value itself is valid). Value validity is left to when an actual
protoreflect.Message.Set operation is performed.
* Add special-casing on proto.SetExtension to treat an invalid
message or list as functionally equivalent to Clear. This is to
be more consistent with the legacy SetExtension implementation
which never panicked when given such values.
* Add special-casing on proto.HasExtension to treat a mismatched
extension descriptor as simply not being present in the message.
This is also to be more consistent with the legacy HasExtension
implementation which did the same thing.

Change-Id: Idf0419abf27b9f85d9b92bd2ff8088e25b7990cc
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/229558
Reviewed-by: Damien Neil <dneil@google.com>
2020-04-23 06:01:13 +00:00

142 lines
3.5 KiB
Go

// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package impl
import (
"fmt"
"reflect"
pref "google.golang.org/protobuf/reflect/protoreflect"
)
func newListConverter(t reflect.Type, fd pref.FieldDescriptor) Converter {
switch {
case t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Slice:
return &listPtrConverter{t, newSingularConverter(t.Elem().Elem(), fd)}
case t.Kind() == reflect.Slice:
return &listConverter{t, newSingularConverter(t.Elem(), fd)}
}
panic(fmt.Sprintf("invalid Go type %v for field %v", t, fd.FullName()))
}
type listConverter struct {
goType reflect.Type // []T
c Converter
}
func (c *listConverter) PBValueOf(v reflect.Value) pref.Value {
if v.Type() != c.goType {
panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), c.goType))
}
pv := reflect.New(c.goType)
pv.Elem().Set(v)
return pref.ValueOfList(&listReflect{pv, c.c})
}
func (c *listConverter) GoValueOf(v pref.Value) reflect.Value {
rv := v.List().(*listReflect).v
if rv.IsNil() {
return reflect.Zero(c.goType)
}
return rv.Elem()
}
func (c *listConverter) IsValidPB(v pref.Value) bool {
list, ok := v.Interface().(*listReflect)
if !ok {
return false
}
return list.v.Type().Elem() == c.goType
}
func (c *listConverter) IsValidGo(v reflect.Value) bool {
return v.IsValid() && v.Type() == c.goType
}
func (c *listConverter) New() pref.Value {
return pref.ValueOfList(&listReflect{reflect.New(c.goType), c.c})
}
func (c *listConverter) Zero() pref.Value {
return pref.ValueOfList(&listReflect{reflect.Zero(reflect.PtrTo(c.goType)), c.c})
}
type listPtrConverter struct {
goType reflect.Type // *[]T
c Converter
}
func (c *listPtrConverter) PBValueOf(v reflect.Value) pref.Value {
if v.Type() != c.goType {
panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), c.goType))
}
return pref.ValueOfList(&listReflect{v, c.c})
}
func (c *listPtrConverter) GoValueOf(v pref.Value) reflect.Value {
return v.List().(*listReflect).v
}
func (c *listPtrConverter) IsValidPB(v pref.Value) bool {
list, ok := v.Interface().(*listReflect)
if !ok {
return false
}
return list.v.Type() == c.goType
}
func (c *listPtrConverter) IsValidGo(v reflect.Value) bool {
return v.IsValid() && v.Type() == c.goType
}
func (c *listPtrConverter) New() pref.Value {
return c.PBValueOf(reflect.New(c.goType.Elem()))
}
func (c *listPtrConverter) Zero() pref.Value {
return c.PBValueOf(reflect.Zero(c.goType))
}
type listReflect struct {
v reflect.Value // *[]T
conv Converter
}
func (ls *listReflect) Len() int {
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.Elem().Index(i))
}
func (ls *listReflect) Set(i int, v pref.Value) {
ls.v.Elem().Index(i).Set(ls.conv.GoValueOf(v))
}
func (ls *listReflect) Append(v pref.Value) {
ls.v.Elem().Set(reflect.Append(ls.v.Elem(), ls.conv.GoValueOf(v)))
}
func (ls *listReflect) AppendMutable() pref.Value {
if _, ok := ls.conv.(*messageConverter); !ok {
panic("invalid AppendMutable on list with non-message type")
}
v := ls.NewElement()
ls.Append(v)
return v
}
func (ls *listReflect) Truncate(i int) {
ls.v.Elem().Set(ls.v.Elem().Slice(0, i))
}
func (ls *listReflect) NewElement() pref.Value {
return ls.conv.New()
}
func (ls *listReflect) IsValid() bool {
return !ls.v.IsNil()
}
func (ls *listReflect) protoUnwrap() interface{} {
return ls.v.Interface()
}