2018-09-12 16:20:37 -07:00
|
|
|
// 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"
|
2019-04-22 11:44:49 -07:00
|
|
|
"math"
|
2018-09-12 16:20:37 -07:00
|
|
|
"reflect"
|
2019-08-05 13:20:14 -07:00
|
|
|
"sync"
|
2018-09-12 16:20:37 -07:00
|
|
|
|
2019-04-08 13:52:14 -07:00
|
|
|
"google.golang.org/protobuf/internal/flags"
|
2019-05-13 23:55:40 -07:00
|
|
|
pref "google.golang.org/protobuf/reflect/protoreflect"
|
2019-04-08 13:52:14 -07:00
|
|
|
preg "google.golang.org/protobuf/reflect/protoregistry"
|
2018-09-12 16:20:37 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
type fieldInfo struct {
|
2019-04-25 23:48:08 -07:00
|
|
|
fieldDesc pref.FieldDescriptor
|
|
|
|
|
2019-04-01 13:49:56 -07:00
|
|
|
// These fields are used for protobuf reflection support.
|
2018-12-07 14:28:33 -08:00
|
|
|
has func(pointer) bool
|
2019-04-25 23:48:08 -07:00
|
|
|
clear func(pointer)
|
2018-12-07 14:28:33 -08:00
|
|
|
get func(pointer) pref.Value
|
|
|
|
set func(pointer, pref.Value)
|
2019-04-25 23:48:08 -07:00
|
|
|
mutable func(pointer) pref.Value
|
2018-12-07 14:28:33 -08:00
|
|
|
newMessage func() pref.Message
|
all: add NewField, NewElement, NewValue
Add methods to protoreflect.{Message,List,Map} to constrict values
assignable to a message field, list element, or map value. These
methods return the default value for scalar fields, the zero value for
scalar list elements and map values, and an empty, mutable value for
messages, lists, and maps.
Deprecate the NewMessage methods on these types, which are superseded.
Updates golang/protobuf#879
Change-Id: I0f064f60c89a239330ccea81523f559f14fd2c4f
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/188997
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-08-05 10:48:38 -07:00
|
|
|
newField func() pref.Value
|
2018-09-12 16:20:37 -07:00
|
|
|
}
|
|
|
|
|
internal/impl: add runtime support for aberrant messages
Implement support in the protobuf runtime to better understand
message types that are not generated by the official generator.
In particular:
* Add a best-effort implementation of protobuf reflection for
"non-nullable" fields which are supposed to be represented by *T,
but are instead represented by a T. "Non-nullable" message fields
report presence based on whether the message is the zero Go value.
* We do NOT implement support for "non-nullable" fields in the
table-driven implementation since we assume that the aberrant messages
that we care about have a Marshal and Unmarshal method.
* We better handle custom messages that implement Marshal and Unmarshal,
but do NOT implement Merge. In that case, we implement merge in terms of
a back-to-back marshal and unmarshal.
* We better tolerate the situations where a protobuf message field
cannot be mapped to a Go struct field since the latter is missing.
In such cases, reflection treats the field as if it were unpopulated.
Setting such fields will panic.
This change allows the runtime to handle all message types declared
in the "go.etcd.io/etcd" and "k8s.io" modules where protobuf reflection,
Marshal, Unmarshal, Reset, Merge, and Equal all work.
The only types that still do not fully work are:
* "k8s.io/api/authentication/v1".ExtraValue
* "k8s.io/api/authentication/v1beta1".ExtraValue
* "k8s.io/api/authorization/v1".ExtraValue
* "k8s.io/api/authorization/v1beta1".ExtraValue
* "k8s.io/api/certificates/v1".ExtraValue
* "k8s.io/api/certificates/v1beta1".ExtraValue
* "k8s.io/apimachinery/pkg/apis/meta/v1".MicroTime
* "k8s.io/apimachinery/pkg/apis/meta/v1".Time
* "k8s.io/apimachinery/pkg/apis/meta/v1".Verbs
While Marshal, Unmarshal, Reset, and Merge continue to work,
protobuf reflection and any functionality that depends on it
(e.g., prototext, protojson, Equal, etc.) will not work.
Change-Id: I67a9d2f1bec35248045ad0c16220d02fc2e0e172
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/300869
Trust: Joe Tsai <joetsai@digital-static.net>
Trust: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Damien Neil <dneil@google.com>
2021-03-11 02:50:41 -08:00
|
|
|
func fieldInfoForMissing(fd pref.FieldDescriptor) fieldInfo {
|
|
|
|
// This never occurs for generated message types.
|
|
|
|
// It implies that a hand-crafted type has missing Go fields
|
|
|
|
// for specific protobuf message fields.
|
|
|
|
return fieldInfo{
|
|
|
|
fieldDesc: fd,
|
|
|
|
has: func(p pointer) bool {
|
|
|
|
return false
|
|
|
|
},
|
|
|
|
clear: func(p pointer) {
|
|
|
|
panic("missing Go struct field for " + string(fd.FullName()))
|
|
|
|
},
|
|
|
|
get: func(p pointer) pref.Value {
|
|
|
|
return fd.Default()
|
|
|
|
},
|
|
|
|
set: func(p pointer, v pref.Value) {
|
|
|
|
panic("missing Go struct field for " + string(fd.FullName()))
|
|
|
|
},
|
|
|
|
mutable: func(p pointer) pref.Value {
|
|
|
|
panic("missing Go struct field for " + string(fd.FullName()))
|
|
|
|
},
|
|
|
|
newMessage: func() pref.Message {
|
|
|
|
panic("missing Go struct field for " + string(fd.FullName()))
|
|
|
|
},
|
|
|
|
newField: func() pref.Value {
|
|
|
|
if v := fd.Default(); v.IsValid() {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
panic("missing Go struct field for " + string(fd.FullName()))
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-06 13:05:11 -07:00
|
|
|
func fieldInfoForOneof(fd pref.FieldDescriptor, fs reflect.StructField, x exporter, ot reflect.Type) fieldInfo {
|
2018-10-17 11:46:52 -07:00
|
|
|
ft := fs.Type
|
|
|
|
if ft.Kind() != reflect.Interface {
|
2020-04-22 21:58:56 -07:00
|
|
|
panic(fmt.Sprintf("field %v has invalid type: got %v, want interface kind", fd.FullName(), ft))
|
2018-10-17 11:46:52 -07:00
|
|
|
}
|
|
|
|
if ot.Kind() != reflect.Struct {
|
2020-04-22 21:58:56 -07:00
|
|
|
panic(fmt.Sprintf("field %v has invalid type: got %v, want struct kind", fd.FullName(), ot))
|
2018-10-17 11:46:52 -07:00
|
|
|
}
|
|
|
|
if !reflect.PtrTo(ot).Implements(ft) {
|
2020-04-22 21:58:56 -07:00
|
|
|
panic(fmt.Sprintf("field %v has invalid type: %v does not implement %v", fd.FullName(), ot, ft))
|
2018-10-17 11:46:52 -07:00
|
|
|
}
|
2019-07-17 16:52:10 -07:00
|
|
|
conv := NewConverter(ot.Field(0).Type, fd)
|
|
|
|
isMessage := fd.Message() != nil
|
2019-04-25 23:48:08 -07:00
|
|
|
|
2018-10-17 11:46:52 -07:00
|
|
|
// TODO: Implement unsafe fast path?
|
2019-07-06 13:05:11 -07:00
|
|
|
fieldOffset := offsetOf(fs, x)
|
2018-10-17 11:46:52 -07:00
|
|
|
return fieldInfo{
|
|
|
|
// NOTE: The logic below intentionally assumes that oneof fields are
|
|
|
|
// well-formatted. That is, the oneof interface never contains a
|
|
|
|
// typed nil pointer to one of the wrapper structs.
|
|
|
|
|
2019-04-25 23:48:08 -07:00
|
|
|
fieldDesc: fd,
|
2018-10-17 11:46:52 -07:00
|
|
|
has: func(p pointer) bool {
|
2018-12-01 04:57:09 -08:00
|
|
|
if p.IsNil() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
2019-08-08 19:23:32 -07:00
|
|
|
if rv.IsNil() || rv.Elem().Type().Elem() != ot || rv.Elem().IsNil() {
|
2018-10-17 11:46:52 -07:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
},
|
2019-04-25 23:48:08 -07:00
|
|
|
clear: func(p pointer) {
|
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
|
|
|
if rv.IsNil() || rv.Elem().Type().Elem() != ot {
|
2019-08-08 19:23:32 -07:00
|
|
|
// NOTE: We intentionally don't check for rv.Elem().IsNil()
|
|
|
|
// so that (*OneofWrapperType)(nil) gets cleared to nil.
|
2019-04-25 23:48:08 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
rv.Set(reflect.Zero(rv.Type()))
|
|
|
|
},
|
2018-10-17 11:46:52 -07:00
|
|
|
get: func(p pointer) pref.Value {
|
2018-12-01 04:57:09 -08:00
|
|
|
if p.IsNil() {
|
all: make handling of zero-value composites more consistent
We occasionally need to work with immutable, empty lists, maps, and
messages. Notably, Message.Get on an empty repeated field will return a
"frozen" empty value.
Move handling of these immutable, zero-length composites into Converter,
to unify the behavior of regular and extension fields.
Add a Zero method to Converter, MessageType, and ExtensionType, to
provide a consistent way to get an empty, frozen value of a composite
type. Adding this method to the public {Message,Extension}Type
interfaces does increase our API surface, but lets us (for example)
cleanly represent an empty map as a nil map rather than a non-nil
one wrapped in a frozenMap type.
Drop the frozen{List,Map,Message} types as no longer necessary.
(These types did have support for creating a read-only view of a
non-empty value, but we are not currently using that feature.)
Change-Id: Ia76f149d591da07b40ce75b7404a7ab8a60cb9d8
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/189339
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-08-07 12:21:41 -07:00
|
|
|
return conv.Zero()
|
2018-12-01 04:57:09 -08:00
|
|
|
}
|
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
2019-08-08 19:23:32 -07:00
|
|
|
if rv.IsNil() || rv.Elem().Type().Elem() != ot || rv.Elem().IsNil() {
|
all: make handling of zero-value composites more consistent
We occasionally need to work with immutable, empty lists, maps, and
messages. Notably, Message.Get on an empty repeated field will return a
"frozen" empty value.
Move handling of these immutable, zero-length composites into Converter,
to unify the behavior of regular and extension fields.
Add a Zero method to Converter, MessageType, and ExtensionType, to
provide a consistent way to get an empty, frozen value of a composite
type. Adding this method to the public {Message,Extension}Type
interfaces does increase our API surface, but lets us (for example)
cleanly represent an empty map as a nil map rather than a non-nil
one wrapped in a frozenMap type.
Drop the frozen{List,Map,Message} types as no longer necessary.
(These types did have support for creating a read-only view of a
non-empty value, but we are not currently using that feature.)
Change-Id: Ia76f149d591da07b40ce75b7404a7ab8a60cb9d8
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/189339
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-08-07 12:21:41 -07:00
|
|
|
return conv.Zero()
|
2018-10-17 11:46:52 -07:00
|
|
|
}
|
|
|
|
rv = rv.Elem().Elem().Field(0)
|
2018-11-05 11:42:22 -08:00
|
|
|
return conv.PBValueOf(rv)
|
2018-10-17 11:46:52 -07:00
|
|
|
},
|
|
|
|
set: func(p pointer, v pref.Value) {
|
2018-12-01 04:57:09 -08:00
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
2019-08-08 19:23:32 -07:00
|
|
|
if rv.IsNil() || rv.Elem().Type().Elem() != ot || rv.Elem().IsNil() {
|
2018-10-17 11:46:52 -07:00
|
|
|
rv.Set(reflect.New(ot))
|
|
|
|
}
|
|
|
|
rv = rv.Elem().Elem().Field(0)
|
2018-11-05 11:42:22 -08:00
|
|
|
rv.Set(conv.GoValueOf(v))
|
2018-10-17 11:46:52 -07:00
|
|
|
},
|
2019-04-25 23:48:08 -07:00
|
|
|
mutable: func(p pointer) pref.Value {
|
2019-07-17 16:52:10 -07:00
|
|
|
if !isMessage {
|
2020-04-22 21:58:56 -07:00
|
|
|
panic(fmt.Sprintf("field %v with invalid Mutable call on field with non-composite type", fd.FullName()))
|
2019-04-25 23:48:08 -07:00
|
|
|
}
|
2018-12-01 04:57:09 -08:00
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
2019-08-08 19:23:32 -07:00
|
|
|
if rv.IsNil() || rv.Elem().Type().Elem() != ot || rv.Elem().IsNil() {
|
2019-04-25 23:48:08 -07:00
|
|
|
rv.Set(reflect.New(ot))
|
2018-10-17 11:46:52 -07:00
|
|
|
}
|
2019-04-25 23:48:08 -07:00
|
|
|
rv = rv.Elem().Elem().Field(0)
|
internal/impl: add runtime support for aberrant messages
Implement support in the protobuf runtime to better understand
message types that are not generated by the official generator.
In particular:
* Add a best-effort implementation of protobuf reflection for
"non-nullable" fields which are supposed to be represented by *T,
but are instead represented by a T. "Non-nullable" message fields
report presence based on whether the message is the zero Go value.
* We do NOT implement support for "non-nullable" fields in the
table-driven implementation since we assume that the aberrant messages
that we care about have a Marshal and Unmarshal method.
* We better handle custom messages that implement Marshal and Unmarshal,
but do NOT implement Merge. In that case, we implement merge in terms of
a back-to-back marshal and unmarshal.
* We better tolerate the situations where a protobuf message field
cannot be mapped to a Go struct field since the latter is missing.
In such cases, reflection treats the field as if it were unpopulated.
Setting such fields will panic.
This change allows the runtime to handle all message types declared
in the "go.etcd.io/etcd" and "k8s.io" modules where protobuf reflection,
Marshal, Unmarshal, Reset, Merge, and Equal all work.
The only types that still do not fully work are:
* "k8s.io/api/authentication/v1".ExtraValue
* "k8s.io/api/authentication/v1beta1".ExtraValue
* "k8s.io/api/authorization/v1".ExtraValue
* "k8s.io/api/authorization/v1beta1".ExtraValue
* "k8s.io/api/certificates/v1".ExtraValue
* "k8s.io/api/certificates/v1beta1".ExtraValue
* "k8s.io/apimachinery/pkg/apis/meta/v1".MicroTime
* "k8s.io/apimachinery/pkg/apis/meta/v1".Time
* "k8s.io/apimachinery/pkg/apis/meta/v1".Verbs
While Marshal, Unmarshal, Reset, and Merge continue to work,
protobuf reflection and any functionality that depends on it
(e.g., prototext, protojson, Equal, etc.) will not work.
Change-Id: I67a9d2f1bec35248045ad0c16220d02fc2e0e172
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/300869
Trust: Joe Tsai <joetsai@digital-static.net>
Trust: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Damien Neil <dneil@google.com>
2021-03-11 02:50:41 -08:00
|
|
|
if rv.Kind() == reflect.Ptr && rv.IsNil() {
|
2019-09-17 13:38:48 -07:00
|
|
|
rv.Set(conv.GoValueOf(pref.ValueOfMessage(conv.New().Message())))
|
2019-04-25 23:48:08 -07:00
|
|
|
}
|
|
|
|
return conv.PBValueOf(rv)
|
2018-10-17 11:46:52 -07:00
|
|
|
},
|
2019-07-17 16:52:10 -07:00
|
|
|
newMessage: func() pref.Message {
|
|
|
|
return conv.New().Message()
|
|
|
|
},
|
all: add NewField, NewElement, NewValue
Add methods to protoreflect.{Message,List,Map} to constrict values
assignable to a message field, list element, or map value. These
methods return the default value for scalar fields, the zero value for
scalar list elements and map values, and an empty, mutable value for
messages, lists, and maps.
Deprecate the NewMessage methods on these types, which are superseded.
Updates golang/protobuf#879
Change-Id: I0f064f60c89a239330ccea81523f559f14fd2c4f
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/188997
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-08-05 10:48:38 -07:00
|
|
|
newField: func() pref.Value {
|
|
|
|
return conv.New()
|
|
|
|
},
|
2018-10-17 11:46:52 -07:00
|
|
|
}
|
2018-09-12 16:20:37 -07:00
|
|
|
}
|
|
|
|
|
2019-07-06 13:05:11 -07:00
|
|
|
func fieldInfoForMap(fd pref.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
|
2018-10-17 00:27:21 +00:00
|
|
|
ft := fs.Type
|
|
|
|
if ft.Kind() != reflect.Map {
|
2020-04-22 21:58:56 -07:00
|
|
|
panic(fmt.Sprintf("field %v has invalid type: got %v, want map kind", fd.FullName(), ft))
|
2018-10-17 00:27:21 +00:00
|
|
|
}
|
2019-07-17 16:52:10 -07:00
|
|
|
conv := NewConverter(ft, fd)
|
2019-04-25 23:48:08 -07:00
|
|
|
|
2018-10-17 00:27:21 +00:00
|
|
|
// TODO: Implement unsafe fast path?
|
2019-07-06 13:05:11 -07:00
|
|
|
fieldOffset := offsetOf(fs, x)
|
2018-10-17 00:27:21 +00:00
|
|
|
return fieldInfo{
|
2019-04-25 23:48:08 -07:00
|
|
|
fieldDesc: fd,
|
2018-10-17 00:27:21 +00:00
|
|
|
has: func(p pointer) bool {
|
2018-12-01 04:57:09 -08:00
|
|
|
if p.IsNil() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
2018-10-17 00:27:21 +00:00
|
|
|
return rv.Len() > 0
|
|
|
|
},
|
2019-04-25 23:48:08 -07:00
|
|
|
clear: func(p pointer) {
|
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
|
|
|
rv.Set(reflect.Zero(rv.Type()))
|
|
|
|
},
|
2018-10-17 00:27:21 +00:00
|
|
|
get: func(p pointer) pref.Value {
|
2018-12-01 04:57:09 -08:00
|
|
|
if p.IsNil() {
|
all: make handling of zero-value composites more consistent
We occasionally need to work with immutable, empty lists, maps, and
messages. Notably, Message.Get on an empty repeated field will return a
"frozen" empty value.
Move handling of these immutable, zero-length composites into Converter,
to unify the behavior of regular and extension fields.
Add a Zero method to Converter, MessageType, and ExtensionType, to
provide a consistent way to get an empty, frozen value of a composite
type. Adding this method to the public {Message,Extension}Type
interfaces does increase our API surface, but lets us (for example)
cleanly represent an empty map as a nil map rather than a non-nil
one wrapped in a frozenMap type.
Drop the frozen{List,Map,Message} types as no longer necessary.
(These types did have support for creating a read-only view of a
non-empty value, but we are not currently using that feature.)
Change-Id: Ia76f149d591da07b40ce75b7404a7ab8a60cb9d8
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/189339
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-08-07 12:21:41 -07:00
|
|
|
return conv.Zero()
|
2018-12-01 04:57:09 -08:00
|
|
|
}
|
2019-04-25 23:48:08 -07:00
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
2019-11-26 13:31:38 -08:00
|
|
|
if rv.Len() == 0 {
|
|
|
|
return conv.Zero()
|
|
|
|
}
|
2019-07-17 16:52:10 -07:00
|
|
|
return conv.PBValueOf(rv)
|
2018-10-17 00:27:21 +00:00
|
|
|
},
|
|
|
|
set: func(p pointer, v pref.Value) {
|
2018-12-01 04:57:09 -08:00
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
2019-11-26 13:31:38 -08:00
|
|
|
pv := conv.GoValueOf(v)
|
|
|
|
if pv.IsNil() {
|
2020-04-22 21:58:56 -07:00
|
|
|
panic(fmt.Sprintf("map field %v cannot be set with read-only value", fd.FullName()))
|
2019-11-26 13:31:38 -08:00
|
|
|
}
|
|
|
|
rv.Set(pv)
|
2018-10-17 00:27:21 +00:00
|
|
|
},
|
2019-04-25 23:48:08 -07:00
|
|
|
mutable: func(p pointer) pref.Value {
|
2019-07-17 16:52:10 -07:00
|
|
|
v := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
|
|
|
if v.IsNil() {
|
|
|
|
v.Set(reflect.MakeMap(fs.Type))
|
|
|
|
}
|
|
|
|
return conv.PBValueOf(v)
|
2018-10-17 00:27:21 +00:00
|
|
|
},
|
all: add NewField, NewElement, NewValue
Add methods to protoreflect.{Message,List,Map} to constrict values
assignable to a message field, list element, or map value. These
methods return the default value for scalar fields, the zero value for
scalar list elements and map values, and an empty, mutable value for
messages, lists, and maps.
Deprecate the NewMessage methods on these types, which are superseded.
Updates golang/protobuf#879
Change-Id: I0f064f60c89a239330ccea81523f559f14fd2c4f
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/188997
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-08-05 10:48:38 -07:00
|
|
|
newField: func() pref.Value {
|
|
|
|
return conv.New()
|
|
|
|
},
|
2018-10-17 00:27:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-06 13:05:11 -07:00
|
|
|
func fieldInfoForList(fd pref.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
|
2018-09-13 13:24:35 -07:00
|
|
|
ft := fs.Type
|
|
|
|
if ft.Kind() != reflect.Slice {
|
2020-04-22 21:58:56 -07:00
|
|
|
panic(fmt.Sprintf("field %v has invalid type: got %v, want slice kind", fd.FullName(), ft))
|
2018-09-13 13:24:35 -07:00
|
|
|
}
|
2019-07-17 16:52:10 -07:00
|
|
|
conv := NewConverter(reflect.PtrTo(ft), fd)
|
2019-04-25 23:48:08 -07:00
|
|
|
|
2018-09-13 13:24:35 -07:00
|
|
|
// TODO: Implement unsafe fast path?
|
2019-07-06 13:05:11 -07:00
|
|
|
fieldOffset := offsetOf(fs, x)
|
2018-09-13 13:24:35 -07:00
|
|
|
return fieldInfo{
|
2019-04-25 23:48:08 -07:00
|
|
|
fieldDesc: fd,
|
2018-09-13 13:24:35 -07:00
|
|
|
has: func(p pointer) bool {
|
2018-12-01 04:57:09 -08:00
|
|
|
if p.IsNil() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
2018-09-13 13:24:35 -07:00
|
|
|
return rv.Len() > 0
|
|
|
|
},
|
2019-04-25 23:48:08 -07:00
|
|
|
clear: func(p pointer) {
|
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
|
|
|
rv.Set(reflect.Zero(rv.Type()))
|
|
|
|
},
|
2018-09-13 13:24:35 -07:00
|
|
|
get: func(p pointer) pref.Value {
|
2018-12-01 04:57:09 -08:00
|
|
|
if p.IsNil() {
|
all: make handling of zero-value composites more consistent
We occasionally need to work with immutable, empty lists, maps, and
messages. Notably, Message.Get on an empty repeated field will return a
"frozen" empty value.
Move handling of these immutable, zero-length composites into Converter,
to unify the behavior of regular and extension fields.
Add a Zero method to Converter, MessageType, and ExtensionType, to
provide a consistent way to get an empty, frozen value of a composite
type. Adding this method to the public {Message,Extension}Type
interfaces does increase our API surface, but lets us (for example)
cleanly represent an empty map as a nil map rather than a non-nil
one wrapped in a frozenMap type.
Drop the frozen{List,Map,Message} types as no longer necessary.
(These types did have support for creating a read-only view of a
non-empty value, but we are not currently using that feature.)
Change-Id: Ia76f149d591da07b40ce75b7404a7ab8a60cb9d8
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/189339
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-08-07 12:21:41 -07:00
|
|
|
return conv.Zero()
|
2018-12-01 04:57:09 -08:00
|
|
|
}
|
2019-07-17 16:52:10 -07:00
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type)
|
2019-11-26 13:31:38 -08:00
|
|
|
if rv.Elem().Len() == 0 {
|
|
|
|
return conv.Zero()
|
|
|
|
}
|
2019-07-17 16:52:10 -07:00
|
|
|
return conv.PBValueOf(rv)
|
2018-09-13 13:24:35 -07:00
|
|
|
},
|
|
|
|
set: func(p pointer, v pref.Value) {
|
2018-12-01 04:57:09 -08:00
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
2019-11-26 13:31:38 -08:00
|
|
|
pv := conv.GoValueOf(v)
|
|
|
|
if pv.IsNil() {
|
2020-04-22 21:58:56 -07:00
|
|
|
panic(fmt.Sprintf("list field %v cannot be set with read-only value", fd.FullName()))
|
2019-11-26 13:31:38 -08:00
|
|
|
}
|
|
|
|
rv.Set(pv.Elem())
|
2018-09-13 13:24:35 -07:00
|
|
|
},
|
2019-04-25 23:48:08 -07:00
|
|
|
mutable: func(p pointer) pref.Value {
|
2019-07-17 16:52:10 -07:00
|
|
|
v := p.Apply(fieldOffset).AsValueOf(fs.Type)
|
|
|
|
return conv.PBValueOf(v)
|
2018-09-13 13:24:35 -07:00
|
|
|
},
|
all: add NewField, NewElement, NewValue
Add methods to protoreflect.{Message,List,Map} to constrict values
assignable to a message field, list element, or map value. These
methods return the default value for scalar fields, the zero value for
scalar list elements and map values, and an empty, mutable value for
messages, lists, and maps.
Deprecate the NewMessage methods on these types, which are superseded.
Updates golang/protobuf#879
Change-Id: I0f064f60c89a239330ccea81523f559f14fd2c4f
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/188997
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-08-05 10:48:38 -07:00
|
|
|
newField: func() pref.Value {
|
|
|
|
return conv.New()
|
|
|
|
},
|
2018-09-13 13:24:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-10 10:21:12 -07:00
|
|
|
var (
|
|
|
|
nilBytes = reflect.ValueOf([]byte(nil))
|
|
|
|
emptyBytes = reflect.ValueOf([]byte{})
|
|
|
|
)
|
2018-09-12 16:20:37 -07:00
|
|
|
|
2019-07-06 13:05:11 -07:00
|
|
|
func fieldInfoForScalar(fd pref.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
|
2018-09-12 16:20:37 -07:00
|
|
|
ft := fs.Type
|
2020-04-28 14:44:38 -07:00
|
|
|
nullable := fd.HasPresence()
|
2019-07-10 10:21:12 -07:00
|
|
|
isBytes := ft.Kind() == reflect.Slice && ft.Elem().Kind() == reflect.Uint8
|
2018-09-12 16:20:37 -07:00
|
|
|
if nullable {
|
|
|
|
if ft.Kind() != reflect.Ptr && ft.Kind() != reflect.Slice {
|
internal/impl: add runtime support for aberrant messages
Implement support in the protobuf runtime to better understand
message types that are not generated by the official generator.
In particular:
* Add a best-effort implementation of protobuf reflection for
"non-nullable" fields which are supposed to be represented by *T,
but are instead represented by a T. "Non-nullable" message fields
report presence based on whether the message is the zero Go value.
* We do NOT implement support for "non-nullable" fields in the
table-driven implementation since we assume that the aberrant messages
that we care about have a Marshal and Unmarshal method.
* We better handle custom messages that implement Marshal and Unmarshal,
but do NOT implement Merge. In that case, we implement merge in terms of
a back-to-back marshal and unmarshal.
* We better tolerate the situations where a protobuf message field
cannot be mapped to a Go struct field since the latter is missing.
In such cases, reflection treats the field as if it were unpopulated.
Setting such fields will panic.
This change allows the runtime to handle all message types declared
in the "go.etcd.io/etcd" and "k8s.io" modules where protobuf reflection,
Marshal, Unmarshal, Reset, Merge, and Equal all work.
The only types that still do not fully work are:
* "k8s.io/api/authentication/v1".ExtraValue
* "k8s.io/api/authentication/v1beta1".ExtraValue
* "k8s.io/api/authorization/v1".ExtraValue
* "k8s.io/api/authorization/v1beta1".ExtraValue
* "k8s.io/api/certificates/v1".ExtraValue
* "k8s.io/api/certificates/v1beta1".ExtraValue
* "k8s.io/apimachinery/pkg/apis/meta/v1".MicroTime
* "k8s.io/apimachinery/pkg/apis/meta/v1".Time
* "k8s.io/apimachinery/pkg/apis/meta/v1".Verbs
While Marshal, Unmarshal, Reset, and Merge continue to work,
protobuf reflection and any functionality that depends on it
(e.g., prototext, protojson, Equal, etc.) will not work.
Change-Id: I67a9d2f1bec35248045ad0c16220d02fc2e0e172
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/300869
Trust: Joe Tsai <joetsai@digital-static.net>
Trust: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Damien Neil <dneil@google.com>
2021-03-11 02:50:41 -08:00
|
|
|
// This never occurs for generated message types.
|
|
|
|
// Despite the protobuf type system specifying presence,
|
|
|
|
// the Go field type cannot represent it.
|
|
|
|
nullable = false
|
2018-09-12 16:20:37 -07:00
|
|
|
}
|
|
|
|
if ft.Kind() == reflect.Ptr {
|
|
|
|
ft = ft.Elem()
|
|
|
|
}
|
|
|
|
}
|
2019-07-17 16:52:10 -07:00
|
|
|
conv := NewConverter(ft, fd)
|
2019-04-25 23:48:08 -07:00
|
|
|
|
2018-09-12 16:20:37 -07:00
|
|
|
// TODO: Implement unsafe fast path?
|
2019-07-06 13:05:11 -07:00
|
|
|
fieldOffset := offsetOf(fs, x)
|
2018-09-12 16:20:37 -07:00
|
|
|
return fieldInfo{
|
2019-04-25 23:48:08 -07:00
|
|
|
fieldDesc: fd,
|
2018-09-12 16:20:37 -07:00
|
|
|
has: func(p pointer) bool {
|
2018-12-01 04:57:09 -08:00
|
|
|
if p.IsNil() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
2018-09-12 16:20:37 -07:00
|
|
|
if nullable {
|
|
|
|
return !rv.IsNil()
|
|
|
|
}
|
|
|
|
switch rv.Kind() {
|
|
|
|
case reflect.Bool:
|
|
|
|
return rv.Bool()
|
|
|
|
case reflect.Int32, reflect.Int64:
|
2018-11-19 15:27:09 -08:00
|
|
|
return rv.Int() != 0
|
2018-09-12 16:20:37 -07:00
|
|
|
case reflect.Uint32, reflect.Uint64:
|
2018-11-19 15:27:09 -08:00
|
|
|
return rv.Uint() != 0
|
2018-09-12 16:20:37 -07:00
|
|
|
case reflect.Float32, reflect.Float64:
|
2019-04-22 11:44:49 -07:00
|
|
|
return rv.Float() != 0 || math.Signbit(rv.Float())
|
2018-09-12 16:20:37 -07:00
|
|
|
case reflect.String, reflect.Slice:
|
|
|
|
return rv.Len() > 0
|
|
|
|
default:
|
2020-04-22 21:58:56 -07:00
|
|
|
panic(fmt.Sprintf("field %v has invalid type: %v", fd.FullName(), rv.Type())) // should never happen
|
2018-09-12 16:20:37 -07:00
|
|
|
}
|
|
|
|
},
|
2019-04-25 23:48:08 -07:00
|
|
|
clear: func(p pointer) {
|
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
|
|
|
rv.Set(reflect.Zero(rv.Type()))
|
|
|
|
},
|
2018-09-12 16:20:37 -07:00
|
|
|
get: func(p pointer) pref.Value {
|
2018-12-01 04:57:09 -08:00
|
|
|
if p.IsNil() {
|
all: make handling of zero-value composites more consistent
We occasionally need to work with immutable, empty lists, maps, and
messages. Notably, Message.Get on an empty repeated field will return a
"frozen" empty value.
Move handling of these immutable, zero-length composites into Converter,
to unify the behavior of regular and extension fields.
Add a Zero method to Converter, MessageType, and ExtensionType, to
provide a consistent way to get an empty, frozen value of a composite
type. Adding this method to the public {Message,Extension}Type
interfaces does increase our API surface, but lets us (for example)
cleanly represent an empty map as a nil map rather than a non-nil
one wrapped in a frozenMap type.
Drop the frozen{List,Map,Message} types as no longer necessary.
(These types did have support for creating a read-only view of a
non-empty value, but we are not currently using that feature.)
Change-Id: Ia76f149d591da07b40ce75b7404a7ab8a60cb9d8
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/189339
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-08-07 12:21:41 -07:00
|
|
|
return conv.Zero()
|
2018-12-01 04:57:09 -08:00
|
|
|
}
|
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
2018-09-12 16:20:37 -07:00
|
|
|
if nullable {
|
|
|
|
if rv.IsNil() {
|
all: make handling of zero-value composites more consistent
We occasionally need to work with immutable, empty lists, maps, and
messages. Notably, Message.Get on an empty repeated field will return a
"frozen" empty value.
Move handling of these immutable, zero-length composites into Converter,
to unify the behavior of regular and extension fields.
Add a Zero method to Converter, MessageType, and ExtensionType, to
provide a consistent way to get an empty, frozen value of a composite
type. Adding this method to the public {Message,Extension}Type
interfaces does increase our API surface, but lets us (for example)
cleanly represent an empty map as a nil map rather than a non-nil
one wrapped in a frozenMap type.
Drop the frozen{List,Map,Message} types as no longer necessary.
(These types did have support for creating a read-only view of a
non-empty value, but we are not currently using that feature.)
Change-Id: Ia76f149d591da07b40ce75b7404a7ab8a60cb9d8
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/189339
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-08-07 12:21:41 -07:00
|
|
|
return conv.Zero()
|
2018-09-12 16:20:37 -07:00
|
|
|
}
|
|
|
|
if rv.Kind() == reflect.Ptr {
|
|
|
|
rv = rv.Elem()
|
|
|
|
}
|
|
|
|
}
|
2018-11-05 11:42:22 -08:00
|
|
|
return conv.PBValueOf(rv)
|
2018-09-12 16:20:37 -07:00
|
|
|
},
|
|
|
|
set: func(p pointer, v pref.Value) {
|
2018-12-01 04:57:09 -08:00
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
2018-09-12 16:20:37 -07:00
|
|
|
if nullable && rv.Kind() == reflect.Ptr {
|
|
|
|
if rv.IsNil() {
|
|
|
|
rv.Set(reflect.New(ft))
|
|
|
|
}
|
|
|
|
rv = rv.Elem()
|
|
|
|
}
|
2018-11-05 11:42:22 -08:00
|
|
|
rv.Set(conv.GoValueOf(v))
|
2019-07-10 10:21:12 -07:00
|
|
|
if isBytes && rv.Len() == 0 {
|
|
|
|
if nullable {
|
2020-04-28 14:44:38 -07:00
|
|
|
rv.Set(emptyBytes) // preserve presence
|
2019-07-10 10:21:12 -07:00
|
|
|
} else {
|
2020-04-28 14:44:38 -07:00
|
|
|
rv.Set(nilBytes) // do not preserve presence
|
2019-07-10 10:21:12 -07:00
|
|
|
}
|
2018-09-12 16:20:37 -07:00
|
|
|
}
|
|
|
|
},
|
all: add NewField, NewElement, NewValue
Add methods to protoreflect.{Message,List,Map} to constrict values
assignable to a message field, list element, or map value. These
methods return the default value for scalar fields, the zero value for
scalar list elements and map values, and an empty, mutable value for
messages, lists, and maps.
Deprecate the NewMessage methods on these types, which are superseded.
Updates golang/protobuf#879
Change-Id: I0f064f60c89a239330ccea81523f559f14fd2c4f
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/188997
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-08-05 10:48:38 -07:00
|
|
|
newField: func() pref.Value {
|
|
|
|
return conv.New()
|
|
|
|
},
|
2018-09-12 16:20:37 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-08 13:52:14 -07:00
|
|
|
func fieldInfoForWeakMessage(fd pref.FieldDescriptor, weakOffset offset) fieldInfo {
|
2019-08-08 13:31:59 -07:00
|
|
|
if !flags.ProtoLegacy {
|
2019-04-08 13:52:14 -07:00
|
|
|
panic("no support for proto1 weak fields")
|
|
|
|
}
|
|
|
|
|
2019-08-05 13:20:14 -07:00
|
|
|
var once sync.Once
|
|
|
|
var messageType pref.MessageType
|
|
|
|
lazyInit := func() {
|
|
|
|
once.Do(func() {
|
|
|
|
messageName := fd.Message().FullName()
|
|
|
|
messageType, _ = preg.GlobalTypes.FindMessageByName(messageName)
|
|
|
|
if messageType == nil {
|
2020-04-22 21:58:56 -07:00
|
|
|
panic(fmt.Sprintf("weak message %v for field %v is not linked in", messageName, fd.FullName()))
|
2019-08-05 13:20:14 -07:00
|
|
|
}
|
|
|
|
})
|
2019-04-08 13:52:14 -07:00
|
|
|
}
|
|
|
|
|
2019-10-01 13:05:16 -07:00
|
|
|
num := fd.Number()
|
2019-04-08 13:52:14 -07:00
|
|
|
return fieldInfo{
|
|
|
|
fieldDesc: fd,
|
|
|
|
has: func(p pointer) bool {
|
|
|
|
if p.IsNil() {
|
|
|
|
return false
|
|
|
|
}
|
2019-10-01 13:05:16 -07:00
|
|
|
_, ok := p.Apply(weakOffset).WeakFields().get(num)
|
2019-04-08 13:52:14 -07:00
|
|
|
return ok
|
|
|
|
},
|
|
|
|
clear: func(p pointer) {
|
2019-10-01 13:05:16 -07:00
|
|
|
p.Apply(weakOffset).WeakFields().clear(num)
|
2019-04-08 13:52:14 -07:00
|
|
|
},
|
|
|
|
get: func(p pointer) pref.Value {
|
2019-08-05 13:20:14 -07:00
|
|
|
lazyInit()
|
2019-04-08 13:52:14 -07:00
|
|
|
if p.IsNil() {
|
2019-09-17 13:38:48 -07:00
|
|
|
return pref.ValueOfMessage(messageType.Zero())
|
2019-04-08 13:52:14 -07:00
|
|
|
}
|
2019-10-01 13:05:16 -07:00
|
|
|
m, ok := p.Apply(weakOffset).WeakFields().get(num)
|
2019-04-08 13:52:14 -07:00
|
|
|
if !ok {
|
2019-09-17 13:38:48 -07:00
|
|
|
return pref.ValueOfMessage(messageType.Zero())
|
2019-04-08 13:52:14 -07:00
|
|
|
}
|
2019-10-01 13:05:16 -07:00
|
|
|
return pref.ValueOfMessage(m.ProtoReflect())
|
2019-04-08 13:52:14 -07:00
|
|
|
},
|
|
|
|
set: func(p pointer, v pref.Value) {
|
2019-08-05 13:20:14 -07:00
|
|
|
lazyInit()
|
2019-04-08 13:52:14 -07:00
|
|
|
m := v.Message()
|
|
|
|
if m.Descriptor() != messageType.Descriptor() {
|
2020-04-22 21:58:56 -07:00
|
|
|
if got, want := m.Descriptor().FullName(), messageType.Descriptor().FullName(); got != want {
|
|
|
|
panic(fmt.Sprintf("field %v has mismatching message descriptor: got %v, want %v", fd.FullName(), got, want))
|
|
|
|
}
|
|
|
|
panic(fmt.Sprintf("field %v has mismatching message descriptor: %v", fd.FullName(), m.Descriptor().FullName()))
|
2019-04-08 13:52:14 -07:00
|
|
|
}
|
2019-10-01 13:05:16 -07:00
|
|
|
p.Apply(weakOffset).WeakFields().set(num, m.Interface())
|
2019-04-08 13:52:14 -07:00
|
|
|
},
|
|
|
|
mutable: func(p pointer) pref.Value {
|
2019-08-05 13:20:14 -07:00
|
|
|
lazyInit()
|
2019-04-08 13:52:14 -07:00
|
|
|
fs := p.Apply(weakOffset).WeakFields()
|
2019-10-01 13:05:16 -07:00
|
|
|
m, ok := fs.get(num)
|
2019-04-08 13:52:14 -07:00
|
|
|
if !ok {
|
2019-10-01 13:05:16 -07:00
|
|
|
m = messageType.New().Interface()
|
|
|
|
fs.set(num, m)
|
2019-04-08 13:52:14 -07:00
|
|
|
}
|
2019-10-01 13:05:16 -07:00
|
|
|
return pref.ValueOfMessage(m.ProtoReflect())
|
2019-04-08 13:52:14 -07:00
|
|
|
},
|
|
|
|
newMessage: func() pref.Message {
|
2019-08-05 13:20:14 -07:00
|
|
|
lazyInit()
|
2019-04-08 13:52:14 -07:00
|
|
|
return messageType.New()
|
|
|
|
},
|
all: add NewField, NewElement, NewValue
Add methods to protoreflect.{Message,List,Map} to constrict values
assignable to a message field, list element, or map value. These
methods return the default value for scalar fields, the zero value for
scalar list elements and map values, and an empty, mutable value for
messages, lists, and maps.
Deprecate the NewMessage methods on these types, which are superseded.
Updates golang/protobuf#879
Change-Id: I0f064f60c89a239330ccea81523f559f14fd2c4f
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/188997
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-08-05 10:48:38 -07:00
|
|
|
newField: func() pref.Value {
|
|
|
|
lazyInit()
|
2019-09-17 13:38:48 -07:00
|
|
|
return pref.ValueOfMessage(messageType.New())
|
all: add NewField, NewElement, NewValue
Add methods to protoreflect.{Message,List,Map} to constrict values
assignable to a message field, list element, or map value. These
methods return the default value for scalar fields, the zero value for
scalar list elements and map values, and an empty, mutable value for
messages, lists, and maps.
Deprecate the NewMessage methods on these types, which are superseded.
Updates golang/protobuf#879
Change-Id: I0f064f60c89a239330ccea81523f559f14fd2c4f
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/188997
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-08-05 10:48:38 -07:00
|
|
|
},
|
2019-04-08 13:52:14 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-06 13:05:11 -07:00
|
|
|
func fieldInfoForMessage(fd pref.FieldDescriptor, fs reflect.StructField, x exporter) fieldInfo {
|
2018-10-19 16:27:46 -07:00
|
|
|
ft := fs.Type
|
2019-07-17 16:52:10 -07:00
|
|
|
conv := NewConverter(ft, fd)
|
2019-04-25 23:48:08 -07:00
|
|
|
|
|
|
|
// TODO: Implement unsafe fast path?
|
2019-07-06 13:05:11 -07:00
|
|
|
fieldOffset := offsetOf(fs, x)
|
2018-10-19 16:27:46 -07:00
|
|
|
return fieldInfo{
|
2019-04-25 23:48:08 -07:00
|
|
|
fieldDesc: fd,
|
2018-10-19 16:27:46 -07:00
|
|
|
has: func(p pointer) bool {
|
2018-12-01 04:57:09 -08:00
|
|
|
if p.IsNil() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
internal/impl: add runtime support for aberrant messages
Implement support in the protobuf runtime to better understand
message types that are not generated by the official generator.
In particular:
* Add a best-effort implementation of protobuf reflection for
"non-nullable" fields which are supposed to be represented by *T,
but are instead represented by a T. "Non-nullable" message fields
report presence based on whether the message is the zero Go value.
* We do NOT implement support for "non-nullable" fields in the
table-driven implementation since we assume that the aberrant messages
that we care about have a Marshal and Unmarshal method.
* We better handle custom messages that implement Marshal and Unmarshal,
but do NOT implement Merge. In that case, we implement merge in terms of
a back-to-back marshal and unmarshal.
* We better tolerate the situations where a protobuf message field
cannot be mapped to a Go struct field since the latter is missing.
In such cases, reflection treats the field as if it were unpopulated.
Setting such fields will panic.
This change allows the runtime to handle all message types declared
in the "go.etcd.io/etcd" and "k8s.io" modules where protobuf reflection,
Marshal, Unmarshal, Reset, Merge, and Equal all work.
The only types that still do not fully work are:
* "k8s.io/api/authentication/v1".ExtraValue
* "k8s.io/api/authentication/v1beta1".ExtraValue
* "k8s.io/api/authorization/v1".ExtraValue
* "k8s.io/api/authorization/v1beta1".ExtraValue
* "k8s.io/api/certificates/v1".ExtraValue
* "k8s.io/api/certificates/v1beta1".ExtraValue
* "k8s.io/apimachinery/pkg/apis/meta/v1".MicroTime
* "k8s.io/apimachinery/pkg/apis/meta/v1".Time
* "k8s.io/apimachinery/pkg/apis/meta/v1".Verbs
While Marshal, Unmarshal, Reset, and Merge continue to work,
protobuf reflection and any functionality that depends on it
(e.g., prototext, protojson, Equal, etc.) will not work.
Change-Id: I67a9d2f1bec35248045ad0c16220d02fc2e0e172
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/300869
Trust: Joe Tsai <joetsai@digital-static.net>
Trust: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Damien Neil <dneil@google.com>
2021-03-11 02:50:41 -08:00
|
|
|
if fs.Type.Kind() != reflect.Ptr {
|
|
|
|
return !isZero(rv)
|
|
|
|
}
|
2018-10-19 16:27:46 -07:00
|
|
|
return !rv.IsNil()
|
|
|
|
},
|
2019-04-25 23:48:08 -07:00
|
|
|
clear: func(p pointer) {
|
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
|
|
|
rv.Set(reflect.Zero(rv.Type()))
|
|
|
|
},
|
2018-10-19 16:27:46 -07:00
|
|
|
get: func(p pointer) pref.Value {
|
2018-12-01 04:57:09 -08:00
|
|
|
if p.IsNil() {
|
all: make handling of zero-value composites more consistent
We occasionally need to work with immutable, empty lists, maps, and
messages. Notably, Message.Get on an empty repeated field will return a
"frozen" empty value.
Move handling of these immutable, zero-length composites into Converter,
to unify the behavior of regular and extension fields.
Add a Zero method to Converter, MessageType, and ExtensionType, to
provide a consistent way to get an empty, frozen value of a composite
type. Adding this method to the public {Message,Extension}Type
interfaces does increase our API surface, but lets us (for example)
cleanly represent an empty map as a nil map rather than a non-nil
one wrapped in a frozenMap type.
Drop the frozen{List,Map,Message} types as no longer necessary.
(These types did have support for creating a read-only view of a
non-empty value, but we are not currently using that feature.)
Change-Id: Ia76f149d591da07b40ce75b7404a7ab8a60cb9d8
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/189339
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-08-07 12:21:41 -07:00
|
|
|
return conv.Zero()
|
2018-12-01 04:57:09 -08:00
|
|
|
}
|
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
2018-11-05 11:42:22 -08:00
|
|
|
return conv.PBValueOf(rv)
|
2018-10-19 16:27:46 -07:00
|
|
|
},
|
|
|
|
set: func(p pointer, v pref.Value) {
|
2018-12-01 04:57:09 -08:00
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
2018-11-05 11:42:22 -08:00
|
|
|
rv.Set(conv.GoValueOf(v))
|
internal/impl: add runtime support for aberrant messages
Implement support in the protobuf runtime to better understand
message types that are not generated by the official generator.
In particular:
* Add a best-effort implementation of protobuf reflection for
"non-nullable" fields which are supposed to be represented by *T,
but are instead represented by a T. "Non-nullable" message fields
report presence based on whether the message is the zero Go value.
* We do NOT implement support for "non-nullable" fields in the
table-driven implementation since we assume that the aberrant messages
that we care about have a Marshal and Unmarshal method.
* We better handle custom messages that implement Marshal and Unmarshal,
but do NOT implement Merge. In that case, we implement merge in terms of
a back-to-back marshal and unmarshal.
* We better tolerate the situations where a protobuf message field
cannot be mapped to a Go struct field since the latter is missing.
In such cases, reflection treats the field as if it were unpopulated.
Setting such fields will panic.
This change allows the runtime to handle all message types declared
in the "go.etcd.io/etcd" and "k8s.io" modules where protobuf reflection,
Marshal, Unmarshal, Reset, Merge, and Equal all work.
The only types that still do not fully work are:
* "k8s.io/api/authentication/v1".ExtraValue
* "k8s.io/api/authentication/v1beta1".ExtraValue
* "k8s.io/api/authorization/v1".ExtraValue
* "k8s.io/api/authorization/v1beta1".ExtraValue
* "k8s.io/api/certificates/v1".ExtraValue
* "k8s.io/api/certificates/v1beta1".ExtraValue
* "k8s.io/apimachinery/pkg/apis/meta/v1".MicroTime
* "k8s.io/apimachinery/pkg/apis/meta/v1".Time
* "k8s.io/apimachinery/pkg/apis/meta/v1".Verbs
While Marshal, Unmarshal, Reset, and Merge continue to work,
protobuf reflection and any functionality that depends on it
(e.g., prototext, protojson, Equal, etc.) will not work.
Change-Id: I67a9d2f1bec35248045ad0c16220d02fc2e0e172
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/300869
Trust: Joe Tsai <joetsai@digital-static.net>
Trust: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Damien Neil <dneil@google.com>
2021-03-11 02:50:41 -08:00
|
|
|
if fs.Type.Kind() == reflect.Ptr && rv.IsNil() {
|
2020-04-22 21:58:56 -07:00
|
|
|
panic(fmt.Sprintf("field %v has invalid nil pointer", fd.FullName()))
|
reflect/protoreflect: clarify Get semantics on unpopulated fields
Clearly specify that Get on an unpopulated field:
* returns the default value for scalars
* returns a mutable (but empty) List for repeated fields
* returns a mutable (but empty) Map for map fields
* returns an invalid value for message fields
The difference in semantics between List+Maps and Messages is because
protobuf semantics provide no distinction between an unpopulated and empty list
or map. On the other hand, there is a semantic difference between an unpopulated
message and an empty message.
Default values for scalars is trivial to implement with FieldDescriptor.Default.
A mutable, but empty List and Map is easy to implement for known fields since
known fields are generated as a slice or map field in a struct.
Since struct fields are addressable, the implementation can just return a
reference to the slice or map.
Repeated, extension fields are a little more tricky since extension fields
are implemented under the hood as a map[FieldNumber]Extension.
Rather than allocating an empty list in KnownFields.Get upon first retrieval
(which presents a race), delegate the work to ExtensionFieldTypes.Register,
which must occur before any Get operation. Register is not a concurrent-safe
operation, so that is an excellent time to initilize empty lists.
The implementation of extensions will need to be careful that Clear on a repeated
field simply truncates it zero instead of deleting the object.
For unpopulated messages, we return an invalid value, instead of the prior
behavior of returning a typed nil-pointer to the Go type for the message.
The approach is problematic because it assumes that
1) all messages are always implemented on a pointer reciever
2) a typed nil-pointer is an appropriate "read-only, but empty" message
These assumptions are not true of all message types (e.g., dynamic messages).
Change-Id: Ie96e6744c890308d9de738b6cf01d3b19e7e7c6a
Reviewed-on: https://go-review.googlesource.com/c/150319
Reviewed-by: Damien Neil <dneil@google.com>
2018-11-19 14:26:06 -08:00
|
|
|
}
|
2018-10-19 16:27:46 -07:00
|
|
|
},
|
2019-04-25 23:48:08 -07:00
|
|
|
mutable: func(p pointer) pref.Value {
|
2018-12-01 04:57:09 -08:00
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
internal/impl: add runtime support for aberrant messages
Implement support in the protobuf runtime to better understand
message types that are not generated by the official generator.
In particular:
* Add a best-effort implementation of protobuf reflection for
"non-nullable" fields which are supposed to be represented by *T,
but are instead represented by a T. "Non-nullable" message fields
report presence based on whether the message is the zero Go value.
* We do NOT implement support for "non-nullable" fields in the
table-driven implementation since we assume that the aberrant messages
that we care about have a Marshal and Unmarshal method.
* We better handle custom messages that implement Marshal and Unmarshal,
but do NOT implement Merge. In that case, we implement merge in terms of
a back-to-back marshal and unmarshal.
* We better tolerate the situations where a protobuf message field
cannot be mapped to a Go struct field since the latter is missing.
In such cases, reflection treats the field as if it were unpopulated.
Setting such fields will panic.
This change allows the runtime to handle all message types declared
in the "go.etcd.io/etcd" and "k8s.io" modules where protobuf reflection,
Marshal, Unmarshal, Reset, Merge, and Equal all work.
The only types that still do not fully work are:
* "k8s.io/api/authentication/v1".ExtraValue
* "k8s.io/api/authentication/v1beta1".ExtraValue
* "k8s.io/api/authorization/v1".ExtraValue
* "k8s.io/api/authorization/v1beta1".ExtraValue
* "k8s.io/api/certificates/v1".ExtraValue
* "k8s.io/api/certificates/v1beta1".ExtraValue
* "k8s.io/apimachinery/pkg/apis/meta/v1".MicroTime
* "k8s.io/apimachinery/pkg/apis/meta/v1".Time
* "k8s.io/apimachinery/pkg/apis/meta/v1".Verbs
While Marshal, Unmarshal, Reset, and Merge continue to work,
protobuf reflection and any functionality that depends on it
(e.g., prototext, protojson, Equal, etc.) will not work.
Change-Id: I67a9d2f1bec35248045ad0c16220d02fc2e0e172
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/300869
Trust: Joe Tsai <joetsai@digital-static.net>
Trust: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Damien Neil <dneil@google.com>
2021-03-11 02:50:41 -08:00
|
|
|
if fs.Type.Kind() == reflect.Ptr && rv.IsNil() {
|
2019-07-17 16:52:10 -07:00
|
|
|
rv.Set(conv.GoValueOf(conv.New()))
|
2019-04-25 23:48:08 -07:00
|
|
|
}
|
|
|
|
return conv.PBValueOf(rv)
|
2018-10-19 16:27:46 -07:00
|
|
|
},
|
2019-07-17 16:52:10 -07:00
|
|
|
newMessage: func() pref.Message {
|
|
|
|
return conv.New().Message()
|
|
|
|
},
|
all: add NewField, NewElement, NewValue
Add methods to protoreflect.{Message,List,Map} to constrict values
assignable to a message field, list element, or map value. These
methods return the default value for scalar fields, the zero value for
scalar list elements and map values, and an empty, mutable value for
messages, lists, and maps.
Deprecate the NewMessage methods on these types, which are superseded.
Updates golang/protobuf#879
Change-Id: I0f064f60c89a239330ccea81523f559f14fd2c4f
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/188997
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-08-05 10:48:38 -07:00
|
|
|
newField: func() pref.Value {
|
|
|
|
return conv.New()
|
|
|
|
},
|
2018-09-12 16:20:37 -07:00
|
|
|
}
|
|
|
|
}
|
2018-12-01 04:57:09 -08:00
|
|
|
|
2019-04-03 13:40:53 -07:00
|
|
|
type oneofInfo struct {
|
2019-04-25 23:48:08 -07:00
|
|
|
oneofDesc pref.OneofDescriptor
|
|
|
|
which func(pointer) pref.FieldNumber
|
2019-04-03 13:40:53 -07:00
|
|
|
}
|
|
|
|
|
2020-04-28 14:44:38 -07:00
|
|
|
func makeOneofInfo(od pref.OneofDescriptor, si structInfo, x exporter) *oneofInfo {
|
|
|
|
oi := &oneofInfo{oneofDesc: od}
|
|
|
|
if od.IsSynthetic() {
|
|
|
|
fs := si.fieldsByNumber[od.Fields().Get(0).Number()]
|
|
|
|
fieldOffset := offsetOf(fs, x)
|
|
|
|
oi.which = func(p pointer) pref.FieldNumber {
|
|
|
|
if p.IsNil() {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
|
|
|
if rv.IsNil() { // valid on either *T or []byte
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return od.Fields().Get(0).Number()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fs := si.oneofsByName[od.Name()]
|
|
|
|
fieldOffset := offsetOf(fs, x)
|
|
|
|
oi.which = func(p pointer) pref.FieldNumber {
|
2019-04-03 13:40:53 -07:00
|
|
|
if p.IsNil() {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
|
|
|
|
if rv.IsNil() {
|
|
|
|
return 0
|
|
|
|
}
|
2019-09-18 18:08:52 -07:00
|
|
|
rv = rv.Elem()
|
|
|
|
if rv.IsNil() {
|
|
|
|
return 0
|
|
|
|
}
|
2020-04-28 14:44:38 -07:00
|
|
|
return si.oneofWrappersByType[rv.Type().Elem()]
|
|
|
|
}
|
2019-04-03 13:40:53 -07:00
|
|
|
}
|
2020-04-28 14:44:38 -07:00
|
|
|
return oi
|
2019-04-03 13:40:53 -07:00
|
|
|
}
|
internal/impl: add runtime support for aberrant messages
Implement support in the protobuf runtime to better understand
message types that are not generated by the official generator.
In particular:
* Add a best-effort implementation of protobuf reflection for
"non-nullable" fields which are supposed to be represented by *T,
but are instead represented by a T. "Non-nullable" message fields
report presence based on whether the message is the zero Go value.
* We do NOT implement support for "non-nullable" fields in the
table-driven implementation since we assume that the aberrant messages
that we care about have a Marshal and Unmarshal method.
* We better handle custom messages that implement Marshal and Unmarshal,
but do NOT implement Merge. In that case, we implement merge in terms of
a back-to-back marshal and unmarshal.
* We better tolerate the situations where a protobuf message field
cannot be mapped to a Go struct field since the latter is missing.
In such cases, reflection treats the field as if it were unpopulated.
Setting such fields will panic.
This change allows the runtime to handle all message types declared
in the "go.etcd.io/etcd" and "k8s.io" modules where protobuf reflection,
Marshal, Unmarshal, Reset, Merge, and Equal all work.
The only types that still do not fully work are:
* "k8s.io/api/authentication/v1".ExtraValue
* "k8s.io/api/authentication/v1beta1".ExtraValue
* "k8s.io/api/authorization/v1".ExtraValue
* "k8s.io/api/authorization/v1beta1".ExtraValue
* "k8s.io/api/certificates/v1".ExtraValue
* "k8s.io/api/certificates/v1beta1".ExtraValue
* "k8s.io/apimachinery/pkg/apis/meta/v1".MicroTime
* "k8s.io/apimachinery/pkg/apis/meta/v1".Time
* "k8s.io/apimachinery/pkg/apis/meta/v1".Verbs
While Marshal, Unmarshal, Reset, and Merge continue to work,
protobuf reflection and any functionality that depends on it
(e.g., prototext, protojson, Equal, etc.) will not work.
Change-Id: I67a9d2f1bec35248045ad0c16220d02fc2e0e172
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/300869
Trust: Joe Tsai <joetsai@digital-static.net>
Trust: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Damien Neil <dneil@google.com>
2021-03-11 02:50:41 -08:00
|
|
|
|
|
|
|
// isZero is identical to reflect.Value.IsZero.
|
|
|
|
// TODO: Remove this when Go1.13 is the minimally supported Go version.
|
|
|
|
func isZero(v reflect.Value) bool {
|
|
|
|
switch v.Kind() {
|
|
|
|
case reflect.Bool:
|
|
|
|
return !v.Bool()
|
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
|
|
return v.Int() == 0
|
|
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
|
|
return v.Uint() == 0
|
|
|
|
case reflect.Float32, reflect.Float64:
|
|
|
|
return math.Float64bits(v.Float()) == 0
|
|
|
|
case reflect.Complex64, reflect.Complex128:
|
|
|
|
c := v.Complex()
|
|
|
|
return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0
|
|
|
|
case reflect.Array:
|
|
|
|
for i := 0; i < v.Len(); i++ {
|
|
|
|
if !isZero(v.Index(i)) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
|
|
|
|
return v.IsNil()
|
|
|
|
case reflect.String:
|
|
|
|
return v.Len() == 0
|
|
|
|
case reflect.Struct:
|
|
|
|
for i := 0; i < v.NumField(); i++ {
|
|
|
|
if !isZero(v.Field(i)) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
panic(&reflect.ValueError{"reflect.Value.IsZero", v.Kind()})
|
|
|
|
}
|
|
|
|
}
|